mirror of
https://github.com/restic/rest-server.git
synced 2025-12-07 09:36:13 -08:00
Compare commits
124 Commits
v0.12.1
...
2f31e10ceb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f31e10ceb | ||
|
|
ad130de021 | ||
|
|
2aaa048aba | ||
|
|
b6ec6f45cc | ||
|
|
2a77536ce5 | ||
|
|
0adcfa2619 | ||
|
|
9f8bb0c87c | ||
|
|
5faeedf050 | ||
|
|
7294612990 | ||
|
|
25066228ee | ||
|
|
72a7319fae | ||
|
|
df5330773f | ||
|
|
2bb4d251e2 | ||
|
|
f018e99109 | ||
|
|
95538fe956 | ||
|
|
4e6193ceee | ||
|
|
4c368ae1fb | ||
|
|
0ed9de379e | ||
|
|
451c4831f9 | ||
|
|
1610cf6cef | ||
|
|
3d35116b3c | ||
|
|
eee73d3bc1 | ||
|
|
df7b13e18f | ||
|
|
2d3e02017b | ||
|
|
2b6f0b39fc | ||
|
|
dbf5253ac2 | ||
|
|
fa15677855 | ||
|
|
19aa0845c0 | ||
|
|
04b52b0cee | ||
|
|
f2d406ff2e | ||
|
|
8ad7cfa60a | ||
|
|
4f17744d6c | ||
|
|
68ae5d1c0b | ||
|
|
0dfc772cdb | ||
|
|
b0a9a0452e | ||
|
|
f053e33486 | ||
|
|
10a06dcbf1 | ||
|
|
b05b44cb2c | ||
|
|
a976a2145b | ||
|
|
751d2841f3 | ||
|
|
5938f9aacd | ||
|
|
376392a89c | ||
|
|
13f461740d | ||
|
|
e9e6529345 | ||
|
|
a0110bb902 | ||
|
|
82c5a314f9 | ||
|
|
9fc5066fc4 | ||
|
|
3e0737a8bd | ||
|
|
9195526406 | ||
|
|
2513a698f3 | ||
|
|
bdfb047edf | ||
|
|
e35c6e39d9 | ||
|
|
da5bb66030 | ||
|
|
664d997006 | ||
|
|
5d7b581db6 | ||
|
|
37b4327012 | ||
|
|
238eceb2a1 | ||
|
|
9fb75a71cb | ||
|
|
38f29da143 | ||
|
|
eb9f8cfa1f | ||
|
|
e8a9fbc88f | ||
|
|
7b2639b21e | ||
|
|
40fd34e7c8 | ||
|
|
a6323b5e98 | ||
|
|
318f0c6a89 | ||
|
|
add9b066f9 | ||
|
|
e35f649f07 | ||
|
|
e309a92895 | ||
|
|
848e75c753 | ||
|
|
d3fedbefe5 | ||
|
|
d8cbfd1f63 | ||
|
|
154efcee43 | ||
|
|
9d5b956858 | ||
|
|
705c83a714 | ||
|
|
421c542e31 | ||
|
|
c510ac2e41 | ||
|
|
fed9c4b1e3 | ||
|
|
a2b39539a3 | ||
|
|
16262def42 | ||
|
|
0c735187ba | ||
|
|
b4430d3607 | ||
|
|
e0674c6150 | ||
|
|
55f43b815c | ||
|
|
b3522ae2ac | ||
|
|
6faaaa713e | ||
|
|
a4f2823515 | ||
|
|
13083acc1a | ||
|
|
ce15402f74 | ||
|
|
b28bea1de4 | ||
|
|
4bfb2eaa81 | ||
|
|
8becd574cb | ||
|
|
03ad2420db | ||
|
|
3ce6aaf2b6 | ||
|
|
e388fcddde | ||
|
|
2d456efbe8 | ||
|
|
14f70457cf | ||
|
|
29753193ac | ||
|
|
e2d5251cbf | ||
|
|
f36b6504fc | ||
|
|
04418c721f | ||
|
|
c615b9dbdd | ||
|
|
292556a8bc | ||
|
|
ef8998a1cc | ||
|
|
0c905fd64c | ||
|
|
45f71173c8 | ||
|
|
ce63a80646 | ||
|
|
6f91a8638d | ||
|
|
59d9e20cbd | ||
|
|
6aa3c966b6 | ||
|
|
228082e5ea | ||
|
|
8523f0c968 | ||
|
|
74b74ed325 | ||
|
|
2dbad90967 | ||
|
|
04eacee642 | ||
|
|
1b1b127490 | ||
|
|
f8669a2eba | ||
|
|
fb5d49c631 | ||
|
|
e6efaaf65f | ||
|
|
8b90047951 | ||
|
|
29fea36169 | ||
|
|
0bb8cd41d1 | ||
|
|
ec2ce8cd27 | ||
|
|
c38e18b708 | ||
|
|
76759aa52e |
39
.github/ISSUE_TEMPLATE/BUG.md
vendored
39
.github/ISSUE_TEMPLATE/BUG.md
vendored
@@ -3,8 +3,8 @@ name: Bug report
|
|||||||
about: Report a problem with rest-server to help us resolve it and improve
|
about: Report a problem with rest-server to help us resolve it and improve
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
Welcome! - We kindly ask that you:
|
Welcome! - We kindly ask that you:
|
||||||
|
|
||||||
1. Fill out the issue template below - not doing so needs a good reason.
|
1. Fill out the issue template below - not doing so needs a good reason.
|
||||||
@@ -24,6 +24,7 @@ for tracking bugs and feature requests directly relating to the development of
|
|||||||
the software itself, rather than the project.
|
the software itself, rather than the project.
|
||||||
|
|
||||||
Thanks for understanding, and for contributing to the project!
|
Thanks for understanding, and for contributing to the project!
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
@@ -31,24 +32,26 @@ Output of `rest-server --version` <!-- If using docker, output of `docker images
|
|||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
|
||||||
How did you run rest-server exactly?
|
|
||||||
------------------------------------
|
Problem description / Steps to reproduce
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
This section should include at least:
|
This section should include at least:
|
||||||
|
|
||||||
|
* A description of the problem you are having with rest-server.
|
||||||
|
|
||||||
* The complete command line and any environment variables you used to
|
* The complete command line and any environment variables you used to
|
||||||
configure rest-server's backend access. Make sure to replace sensitive values!
|
configure rest-server. Make sure to replace sensitive values!
|
||||||
|
|
||||||
* The output of the commands, what rest-server prints gives may give us much
|
* The output of the commands, what rest-server prints gives may give us much
|
||||||
information to diagnose the problem!
|
information to diagnose the problem!
|
||||||
|
|
||||||
|
* The more time you spend describing an easy way to reproduce the behavior (if
|
||||||
|
this is possible), the easier it is for the project developers to fix it!
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
What backend/server/service did you use to store the repository?
|
|
||||||
----------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Expected behavior
|
Expected behavior
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@@ -56,30 +59,20 @@ Expected behavior
|
|||||||
Describe what you'd like rest-server to do differently.
|
Describe what you'd like rest-server to do differently.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
Actual behavior
|
Actual behavior
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Please try to concentrate on observations, so only describe what you observed directly.
|
In this section, please try to concentrate on observations, so only describe
|
||||||
|
what you observed directly.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
Steps to reproduce the behavior
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The more time you spend describing an easy way to reproduce the behavior (if
|
|
||||||
this is possible), the easier it is for the project developers to fix it!
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
Do you have any idea what may have caused this?
|
Do you have any idea what may have caused this?
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
<!--
|
||||||
Do you have an idea how to solve the issue?
|
Did something noteworthy happen on your system, Internet connection, backend services, etc?
|
||||||
-------------------------------------------
|
-->
|
||||||
|
|
||||||
|
|
||||||
Did rest-server help you today? Did it make you happy in any way?
|
Did rest-server help you today? Did it make you happy in any way?
|
||||||
|
|||||||
17
.github/ISSUE_TEMPLATE/FEATURE.md
vendored
17
.github/ISSUE_TEMPLATE/FEATURE.md
vendored
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: Feature request/enhancement
|
name: Feature request
|
||||||
about: Suggest a new feature or enhancement for rest-server
|
about: Suggest a new feature or enhancement for rest-server
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
Welcome! - We kindly ask that you:
|
Welcome! - We kindly ask that you:
|
||||||
|
|
||||||
1. Fill out the issue template below - not doing so needs a good reason.
|
1. Fill out the issue template below - not doing so needs a good reason.
|
||||||
@@ -18,6 +18,7 @@ for tracking bugs and feature requests directly relating to the development of
|
|||||||
the software itself, rather than the project.
|
the software itself, rather than the project.
|
||||||
|
|
||||||
Thanks for understanding, and for contributing to the project!
|
Thanks for understanding, and for contributing to the project!
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
@@ -30,24 +31,22 @@ later to see what has changed in rest-server when we revisit this issue after so
|
|||||||
time.
|
time.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
What should rest-server do differently? Which functionality do you think we should add?
|
||||||
What should rest-server do differently?
|
---------------------------------------------------------------------------------------
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Please describe the feature you'd like to see added or changed here.
|
Please describe the feature you'd like us to add here.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
What are you trying to do? What is your use case?
|
What are you trying to do? What problem would this solve?
|
||||||
-------------------------------------------------
|
---------------------------------------------------------
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
This section should contain a brief description what you're trying to do, which
|
This section should contain a brief description what you're trying to do, which
|
||||||
would be possible after implementing the new feature.
|
would be possible after implementing the new feature.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
Did rest-server help you today? Did it make you happy in any way?
|
Did rest-server help you today? Did it make you happy in any way?
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
51
.github/PULL_REQUEST_TEMPLATE.md
vendored
51
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,42 +1,41 @@
|
|||||||
<!--
|
<!--
|
||||||
Thank you very much for contributing code or documentation to rest-server!
|
Thank you very much for contributing code or documentation to rest-server! Please
|
||||||
|
fill out the following questions to make it easier for us to review your
|
||||||
Please note that each PR should be preceded by an issue where the suggested
|
changes.
|
||||||
change can be discussed in general and without focus on specific code. That
|
|
||||||
way, work done in the PR will better match what's been agreed in the issue.
|
|
||||||
|
|
||||||
Please fill out the following questions to make it easier for us to review
|
|
||||||
your changes. You don't have to check all the checkboxes at once, instead
|
|
||||||
feel free to add more commits over time.
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
What does this PR change? What problem does it solve?
|
||||||
What is the purpose of this change? What does it change?
|
-----------------------------------------------------
|
||||||
--------------------------------------------------------
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Describe the changes here, as detailed as needed.
|
Describe the changes and their purpose here, as detailed as needed.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
Was the change previously discussed in an issue or on the forum?
|
||||||
Was the change discussed in an issue or in the forum before?
|
----------------------------------------------------------------
|
||||||
------------------------------------------------------------
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Link issues and relevant forum posts here.
|
Link issues and relevant forum posts here.
|
||||||
|
|
||||||
If this PR resolves an issue on GitHub, write "Closes #1234" such
|
If this PR resolves an issue on GitHub, use "Closes #1234" so that the issue
|
||||||
that the issue is closed automatically when this PR is merged.
|
is closed automatically when this PR is merged.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
Checklist
|
Checklist
|
||||||
---------
|
---------
|
||||||
|
|
||||||
- [ ] I have enabled [maintainer edits for this PR](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork)
|
<!--
|
||||||
- [ ] I have added tests for all changes in this PR
|
You do not need to check all the boxes below all at once. Feel free to take
|
||||||
- [ ] I have added documentation for the changes (in the manual)
|
your time and add more commits. If you're done and ready for review, please
|
||||||
- [ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (template [here](https://github.com/restic/rest-server/blob/master/changelog/TEMPLATE))
|
check the last box. Enable a checkbox by replacing [ ] with [x].
|
||||||
- [ ] I have run `gofmt` on the code in all commits
|
|
||||||
- [ ] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/rest-server/commits/master)
|
Please always follow these steps:
|
||||||
- [ ] I'm done, this Pull Request is ready for review
|
- Enable [maintainer edits](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork).
|
||||||
|
- Run `gofmt` on the code in all commits.
|
||||||
|
- Format all commit messages in the same style as [the other commits in the repository](https://github.com/restic/rest-server/blob/master/CONTRIBUTING.md#git-commits).
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I have added tests for all code changes.
|
||||||
|
- [ ] I have added documentation for relevant changes (in the manual).
|
||||||
|
- [ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (see [template](https://github.com/restic/rest-server/blob/master/changelog/TEMPLATE)).
|
||||||
|
- [ ] I'm done! This pull request is ready for review.
|
||||||
|
|||||||
82
.github/workflows/tests.yml
vendored
82
.github/workflows/tests.yml
vendored
@@ -7,48 +7,58 @@ on:
|
|||||||
|
|
||||||
# run tests for all pull requests
|
# run tests for all pull requests
|
||||||
pull_request:
|
pull_request:
|
||||||
|
merge_group:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
latest_go: "1.20.x"
|
latest_go: "1.24.x"
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go:
|
include:
|
||||||
- 1.17.x
|
- job_name: Linux
|
||||||
- 1.18.x
|
go: 1.24.x
|
||||||
- 1.19.x
|
os: ubuntu-latest
|
||||||
- 1.20.x
|
check_changelog: true
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Go ${{ matrix.go }}
|
- job_name: Linux (race)
|
||||||
|
go: 1.24.x
|
||||||
|
os: ubuntu-latest
|
||||||
|
test_opts: "-race"
|
||||||
|
|
||||||
|
- job_name: Linux
|
||||||
|
go: 1.23.x
|
||||||
|
os: ubuntu-latest
|
||||||
|
|
||||||
|
name: ${{ matrix.job_name }} Go ${{ matrix.go }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GOPROXY: https://proxy.golang.org
|
GOPROXY: https://proxy.golang.org
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go ${{ matrix.go }}
|
- name: Set up Go ${{ matrix.go }}
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
go build ./cmd/rest-server
|
|
||||||
|
|
||||||
- name: Build with build.go
|
- name: Build with build.go
|
||||||
run: |
|
run: |
|
||||||
go run build.go --goos linux
|
go run build.go --goos linux
|
||||||
go run build.go --goos windows
|
go run build.go --goos windows
|
||||||
go run build.go --goos darwin
|
go run build.go --goos darwin
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run local Tests
|
||||||
run: |
|
run: |
|
||||||
go test ./...
|
go test -cover ${{matrix.test_opts}} ./...
|
||||||
|
|
||||||
- name: Check changelog files with calens
|
- name: Check changelog files with calens
|
||||||
run: |
|
run: |
|
||||||
@@ -57,27 +67,30 @@ jobs:
|
|||||||
|
|
||||||
echo "check changelog files"
|
echo "check changelog files"
|
||||||
calens
|
calens
|
||||||
|
if: matrix.check_changelog
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: lint
|
name: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
# allow annotating code in the PR
|
||||||
|
checks: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go ${{ env.latest_go }}
|
- name: Set up Go ${{ env.latest_go }}
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.latest_go }}
|
go-version: ${{ env.latest_go }}
|
||||||
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
version: v1.51
|
version: v1.64.8
|
||||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
args: --verbose --timeout 5m
|
||||||
only-new-issues: true
|
|
||||||
args: --verbose --timeout 10m
|
|
||||||
|
|
||||||
# only run golangci-lint for pull requests, otherwise ALL hints get
|
# only run golangci-lint for pull requests, otherwise ALL hints get
|
||||||
# reported. We need to slowly address all issues until we can enable
|
# reported. We need to slowly address all issues until we can enable
|
||||||
@@ -89,3 +102,18 @@ jobs:
|
|||||||
echo "check if go.mod and go.sum are up to date"
|
echo "check if go.mod and go.sum are up to date"
|
||||||
go mod tidy
|
go mod tidy
|
||||||
git diff --exit-code go.mod go.sum
|
git diff --exit-code go.mod go.sum
|
||||||
|
|
||||||
|
analyze:
|
||||||
|
name: Analyze results
|
||||||
|
needs: [test, lint]
|
||||||
|
if: always()
|
||||||
|
|
||||||
|
permissions: # no need to access code
|
||||||
|
contents: none
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Decide whether the needed jobs succeeded or failed
|
||||||
|
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe
|
||||||
|
with:
|
||||||
|
jobs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
@@ -10,13 +10,10 @@ linters:
|
|||||||
# make sure all errors returned by functions are handled
|
# make sure all errors returned by functions are handled
|
||||||
- errcheck
|
- errcheck
|
||||||
|
|
||||||
# find unused code
|
|
||||||
- deadcode
|
|
||||||
|
|
||||||
# show how code can be simplified
|
# show how code can be simplified
|
||||||
- gosimple
|
- gosimple
|
||||||
|
|
||||||
# # make sure code is formatted
|
# make sure code is formatted
|
||||||
- gofmt
|
- gofmt
|
||||||
|
|
||||||
# examine code and report suspicious constructs, such as Printf calls whose
|
# examine code and report suspicious constructs, such as Printf calls whose
|
||||||
@@ -35,15 +32,14 @@ linters:
|
|||||||
# find unused variables, functions, structs, types, etc.
|
# find unused variables, functions, structs, types, etc.
|
||||||
- unused
|
- unused
|
||||||
|
|
||||||
# find unused struct fields
|
|
||||||
- structcheck
|
|
||||||
|
|
||||||
# find unused global variables
|
|
||||||
- varcheck
|
|
||||||
|
|
||||||
# parse and typecheck code
|
# parse and typecheck code
|
||||||
- typecheck
|
- typecheck
|
||||||
|
|
||||||
|
# ensure that http response bodies are closed
|
||||||
|
- bodyclose
|
||||||
|
|
||||||
|
- importas
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
# don't use the default exclude rules, this hides (among others) ignored
|
# don't use the default exclude rules, this hides (among others) ignored
|
||||||
# errors from Close() calls
|
# errors from Close() calls
|
||||||
@@ -55,3 +51,6 @@ issues:
|
|||||||
- exported (function|method|var|type|const) .* should have comment or be unexported
|
- exported (function|method|var|type|const) .* should have comment or be unexported
|
||||||
# revive: ignore constants in all caps
|
# revive: ignore constants in all caps
|
||||||
- don't use ALL_CAPS in Go names; use CamelCase
|
- don't use ALL_CAPS in Go names; use CamelCase
|
||||||
|
# revive: lots of packages don't have such a comment
|
||||||
|
- "package-comments: should have a package comment"
|
||||||
|
- "redefines-builtin-id:"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
version: 2
|
||||||
|
|
||||||
before:
|
before:
|
||||||
# Run a few commands to check the state of things. When anything is changed
|
# Run a few commands to check the state of things. When anything is changed
|
||||||
# in files commited to the repo, goreleaser will abort before building
|
# in files commited to the repo, goreleaser will abort before building
|
||||||
@@ -19,29 +21,27 @@ before:
|
|||||||
|
|
||||||
# build a single binary
|
# build a single binary
|
||||||
builds:
|
builds:
|
||||||
-
|
- id: default
|
||||||
# make sure everything is statically linked by disabling cgo altogether
|
# make sure everything is statically linked by disabling cgo altogether
|
||||||
env:
|
env: &build_env
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
|
|
||||||
# set the package for the main binary
|
# set the package for the main binary
|
||||||
main: ./cmd/rest-server
|
main: ./cmd/rest-server
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
# don't include any paths to source files in the resulting binary
|
&build_flags # don't include any paths to source files in the resulting binary
|
||||||
- -trimpath
|
- -trimpath
|
||||||
|
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||||
|
|
||||||
ldflags:
|
ldflags: &build_ldflags # set the version variable in the main package
|
||||||
# set the version variable in the main package
|
|
||||||
- "-s -w -X main.version={{ .Version }}"
|
- "-s -w -X main.version={{ .Version }}"
|
||||||
|
|
||||||
# list all operating systems and architectures we build binaries for
|
# list all operating systems and architectures we build binaries for
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
- darwin
|
- darwin
|
||||||
- windows
|
|
||||||
- freebsd
|
- freebsd
|
||||||
- netbsd
|
- netbsd
|
||||||
- openbsd
|
- openbsd
|
||||||
@@ -50,7 +50,7 @@ builds:
|
|||||||
|
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- 386
|
- "386"
|
||||||
- arm
|
- arm
|
||||||
- arm64
|
- arm64
|
||||||
- mips
|
- mips
|
||||||
@@ -59,40 +59,63 @@ builds:
|
|||||||
- ppc64
|
- ppc64
|
||||||
- ppc64le
|
- ppc64le
|
||||||
goarm:
|
goarm:
|
||||||
- 6
|
- "6"
|
||||||
- 7
|
- "7"
|
||||||
|
|
||||||
|
- id: windows-only
|
||||||
|
env: *build_env
|
||||||
|
main: ./cmd/rest-server
|
||||||
|
flags: *build_flags
|
||||||
|
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||||
|
ldflags: *build_ldflags
|
||||||
|
goos:
|
||||||
|
- windows
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- "386"
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
|
||||||
# configure the resulting archives to create
|
# configure the resulting archives to create
|
||||||
archives:
|
archives:
|
||||||
-
|
- id: default
|
||||||
|
builds: [default, windows-only]
|
||||||
|
format: tar.gz
|
||||||
# package a directory which contains the source file
|
# package a directory which contains the source file
|
||||||
wrap_in_directory: true
|
wrap_in_directory: true
|
||||||
|
|
||||||
builds_info: &archive_file_info
|
builds_info: &archive_file_info
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mtime: '{{ .CommitDate }}'
|
mtime: "{{ .CommitDate }}"
|
||||||
mode: 0644
|
mode: 0644
|
||||||
|
|
||||||
# add these files to all archives
|
# add these files to all archives
|
||||||
files:
|
files: &archive_files
|
||||||
- src: LICENSE
|
- src: LICENSE
|
||||||
dst: .
|
dst: LICENSE
|
||||||
info: *archive_file_info
|
info: *archive_file_info
|
||||||
- src: README.md
|
- src: README.md
|
||||||
dst: .
|
dst: README.md
|
||||||
info: *archive_file_info
|
info: *archive_file_info
|
||||||
- src: CHANGELOG.md
|
- src: CHANGELOG.md
|
||||||
dst: .
|
dst: CHANGELOG.md
|
||||||
info: *archive_file_info
|
info: *archive_file_info
|
||||||
|
|
||||||
|
- id: windows-only
|
||||||
|
builds: [windows-only]
|
||||||
|
formats: [zip]
|
||||||
|
wrap_in_directory: true
|
||||||
|
builds_info: *archive_file_info
|
||||||
|
files: *archive_files
|
||||||
|
|
||||||
# also build an archive of the source code
|
# also build an archive of the source code
|
||||||
source:
|
source:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
# build a file containing the SHA256 hashes
|
# build a file containing the SHA256 hashes
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'SHA256SUMS'
|
name_template: "SHA256SUMS"
|
||||||
|
|
||||||
# sign the checksum file
|
# sign the checksum file
|
||||||
signs:
|
signs:
|
||||||
@@ -105,10 +128,6 @@ signs:
|
|||||||
- "--detach-sign"
|
- "--detach-sign"
|
||||||
- "${artifact}"
|
- "${artifact}"
|
||||||
|
|
||||||
# do not generate a changelog file, we're using calens for that
|
|
||||||
changelog:
|
|
||||||
skip: true
|
|
||||||
|
|
||||||
# configure building the rest-server docker image
|
# configure building the rest-server docker image
|
||||||
dockers:
|
dockers:
|
||||||
- image_templates:
|
- image_templates:
|
||||||
@@ -130,7 +149,7 @@ dockers:
|
|||||||
- docker/entrypoint.sh
|
- docker/entrypoint.sh
|
||||||
- image_templates:
|
- image_templates:
|
||||||
- restic/rest-server:{{ .Version }}-i386
|
- restic/rest-server:{{ .Version }}-i386
|
||||||
goarch: 386
|
goarch: "386"
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/386"
|
- "--platform=linux/386"
|
||||||
- "--pull"
|
- "--pull"
|
||||||
@@ -206,21 +225,20 @@ dockers:
|
|||||||
dockerfile: "Dockerfile.goreleaser"
|
dockerfile: "Dockerfile.goreleaser"
|
||||||
extra_files: *extra_files
|
extra_files: *extra_files
|
||||||
|
|
||||||
|
|
||||||
docker_manifests:
|
docker_manifests:
|
||||||
- name_template: "restic/rest-server:{{ .Version }}"
|
- name_template: "restic/rest-server:{{ .Version }}"
|
||||||
image_templates:
|
image_templates:
|
||||||
- "restic/rest-server:{{ .Version }}-amd64"
|
- "restic/rest-server:{{ .Version }}-amd64"
|
||||||
- "restic/rest-server:{{ .Version }}-i386"
|
- "restic/rest-server:{{ .Version }}-i386"
|
||||||
- "restic/rest-server:{{ .Version }}-arm32v6"
|
- "restic/rest-server:{{ .Version }}-arm32v6"
|
||||||
- "restic/rest-server:{{ .Version }}-arm32v7"
|
- "restic/rest-server:{{ .Version }}-arm32v7"
|
||||||
- "restic/rest-server:{{ .Version }}-arm64v8"
|
- "restic/rest-server:{{ .Version }}-arm64v8"
|
||||||
- "restic/rest-server:{{ .Version }}-ppc64le"
|
- "restic/rest-server:{{ .Version }}-ppc64le"
|
||||||
- name_template: "restic/rest-server:latest"
|
- name_template: "restic/rest-server:latest"
|
||||||
image_templates:
|
image_templates:
|
||||||
- "restic/rest-server:{{ .Version }}-amd64"
|
- "restic/rest-server:{{ .Version }}-amd64"
|
||||||
- "restic/rest-server:{{ .Version }}-i386"
|
- "restic/rest-server:{{ .Version }}-i386"
|
||||||
- "restic/rest-server:{{ .Version }}-arm32v6"
|
- "restic/rest-server:{{ .Version }}-arm32v6"
|
||||||
- "restic/rest-server:{{ .Version }}-arm32v7"
|
- "restic/rest-server:{{ .Version }}-arm32v7"
|
||||||
- "restic/rest-server:{{ .Version }}-arm64v8"
|
- "restic/rest-server:{{ .Version }}-arm64v8"
|
||||||
- "restic/rest-server:{{ .Version }}-ppc64le"
|
- "restic/rest-server:{{ .Version }}-ppc64le"
|
||||||
|
|||||||
380
CHANGELOG.md
380
CHANGELOG.md
@@ -1,3 +1,160 @@
|
|||||||
|
Changelog for rest-server 0.14.0 (2025-05-31)
|
||||||
|
============================================
|
||||||
|
|
||||||
|
The following sections list the changes in rest-server 0.14.0 relevant
|
||||||
|
to users. The changes are ordered by importance.
|
||||||
|
|
||||||
|
Summary
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Sec #318: Fix world-readable permissions on new `.htpasswd` files
|
||||||
|
* Chg #322: Update dependencies and require Go 1.23 or newer
|
||||||
|
* Enh #174: Support proxy-based authentication
|
||||||
|
* Enh #189: Support group accessible repositories
|
||||||
|
* Enh #295: Output status of append-only mode on startup
|
||||||
|
* Enh #315: Hardened tls settings
|
||||||
|
* Enh #321: Add zip archive format for Windows releases
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Security #318: Fix world-readable permissions on new `.htpasswd` files
|
||||||
|
|
||||||
|
On startup the rest-server Docker container creates an empty `.htpasswd` file if
|
||||||
|
none exists yet. This file was world-readable by default, which can be a
|
||||||
|
security risk, even though the file only contains hashed passwords.
|
||||||
|
|
||||||
|
This has been fixed such that new `.htpasswd` files are no longer
|
||||||
|
world-readabble.
|
||||||
|
|
||||||
|
The permissions of existing `.htpasswd` files must be manually changed if
|
||||||
|
relevant in your setup.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/318
|
||||||
|
https://github.com/restic/rest-server/pull/340
|
||||||
|
|
||||||
|
* Change #322: Update dependencies and require Go 1.23 or newer
|
||||||
|
|
||||||
|
All dependencies have been updated. Rest-server now requires Go 1.23 or newer to
|
||||||
|
build.
|
||||||
|
|
||||||
|
This also disables support for TLS versions older than TLS 1.2. On Windows,
|
||||||
|
rest-server now requires at least Windows 10 or Windows Server 2016. On macOS,
|
||||||
|
rest-server now requires at least macOS 11 Big Sur.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/322
|
||||||
|
https://github.com/restic/rest-server/pull/338
|
||||||
|
|
||||||
|
* Enhancement #174: Support proxy-based authentication
|
||||||
|
|
||||||
|
Rest-server now supports authentication via HTTP proxy headers. This feature can
|
||||||
|
be enabled by specifying the username header using the `--proxy-auth-username`
|
||||||
|
option (e.g., `--proxy-auth-username=X-Forwarded-User`).
|
||||||
|
|
||||||
|
When enabled, the server authenticates users based on the specified header and
|
||||||
|
disables Basic Auth. Note that proxy authentication is disabled when `--no-auth`
|
||||||
|
is set.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/174
|
||||||
|
https://github.com/restic/rest-server/pull/307
|
||||||
|
|
||||||
|
* Enhancement #189: Support group accessible repositories
|
||||||
|
|
||||||
|
Rest-server now supports making repositories accessible to the filesystem group
|
||||||
|
by setting the `--group-accessible-repos` option. Note that permissions of
|
||||||
|
existing files are not modified. To allow the group to read and write file, use
|
||||||
|
a umask of `007`. To only grant read access use `027`. To make an existing
|
||||||
|
repository group-accessible, use `chmod -R g+rwX /path/to/repo`.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/189
|
||||||
|
https://github.com/restic/rest-server/pull/308
|
||||||
|
|
||||||
|
* Enhancement #295: Output status of append-only mode on startup
|
||||||
|
|
||||||
|
Rest-server now displays the status of append-only mode during startup.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/295
|
||||||
|
|
||||||
|
* Enhancement #315: Hardened tls settings
|
||||||
|
|
||||||
|
Rest-server now uses a secure TLS cipher suite set by default. The minimum TLS
|
||||||
|
version is now TLS 1.2 and can be further increased using the new
|
||||||
|
`--tls-min-ver` option, allowing users to enforce stricter security
|
||||||
|
requirements.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/315
|
||||||
|
|
||||||
|
* Enhancement #321: Add zip archive format for Windows releases
|
||||||
|
|
||||||
|
Windows users can now download rest-server binaries in zip archive format (.zip)
|
||||||
|
in addition to the existing tar.gz archives.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/321
|
||||||
|
https://github.com/restic/rest-server/pull/346
|
||||||
|
|
||||||
|
|
||||||
|
Changelog for rest-server 0.13.0 (2024-07-26)
|
||||||
|
============================================
|
||||||
|
|
||||||
|
The following sections list the changes in rest-server 0.13.0 relevant
|
||||||
|
to users. The changes are ordered by importance.
|
||||||
|
|
||||||
|
Summary
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Chg #267: Update dependencies and require Go 1.18 or newer
|
||||||
|
* Chg #273: Shut down cleanly on TERM and INT signals
|
||||||
|
* Enh #271: Print listening address after start-up
|
||||||
|
* Enh #272: Support listening on a unix socket
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Change #267: Update dependencies and require Go 1.18 or newer
|
||||||
|
|
||||||
|
Most dependencies have been updated. Since some libraries require newer language
|
||||||
|
features, support for Go 1.17 has been dropped, which means that rest-server now
|
||||||
|
requires at least Go 1.18 to build.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/267
|
||||||
|
|
||||||
|
* Change #273: Shut down cleanly on TERM and INT signals
|
||||||
|
|
||||||
|
Rest-server now listens for TERM and INT signals and cleanly closes down the
|
||||||
|
http.Server and listener when receiving either of them.
|
||||||
|
|
||||||
|
This is particularly useful when listening on a unix socket, as the server will
|
||||||
|
now remove the socket file when it shuts down.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/273
|
||||||
|
|
||||||
|
* Enhancement #271: Print listening address after start-up
|
||||||
|
|
||||||
|
When started with `--listen :0`, rest-server would print `start server on :0`
|
||||||
|
|
||||||
|
The message now also includes the actual address listened on, for example `start
|
||||||
|
server on 0.0.0.0:37333`. This is useful when starting a server with an
|
||||||
|
auto-allocated free port number (port 0).
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/271
|
||||||
|
|
||||||
|
* Enhancement #272: Support listening on a unix socket
|
||||||
|
|
||||||
|
It is now possible to make rest-server listen on a unix socket by prefixing the
|
||||||
|
socket filename with `unix:` and passing it to the `--listen` option, for
|
||||||
|
example `--listen unix:/tmp/foo`.
|
||||||
|
|
||||||
|
This is useful in combination with remote port forwarding to enable a remote
|
||||||
|
server to backup locally, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
rest-server --listen unix:/tmp/foo &
|
||||||
|
ssh -R /tmp/foo:/tmp/foo user@host restic -r rest:http+unix:///tmp/foo:/repo backup
|
||||||
|
```
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/272
|
||||||
|
|
||||||
|
|
||||||
Changelog for rest-server 0.12.1 (2023-07-09)
|
Changelog for rest-server 0.12.1 (2023-07-09)
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
@@ -16,33 +173,34 @@ Details
|
|||||||
|
|
||||||
* Bugfix #230: Fix erroneous warnings about unsupported fsync
|
* Bugfix #230: Fix erroneous warnings about unsupported fsync
|
||||||
|
|
||||||
Due to a regression in rest-server 0.12.0, it continuously printed `WARNING: fsync is not
|
Due to a regression in rest-server 0.12.0, it continuously printed `WARNING:
|
||||||
supported by the data storage. This can lead to data loss, if the system crashes or the storage is
|
fsync is not supported by the data storage. This can lead to data loss, if the
|
||||||
unexpectedly disconnected.` for systems that support fsync. We have fixed the warning.
|
system crashes or the storage is unexpectedly disconnected.` for systems that
|
||||||
|
support fsync. We have fixed the warning.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/230
|
https://github.com/restic/rest-server/issues/230
|
||||||
https://github.com/restic/rest-server/pull/231
|
https://github.com/restic/rest-server/pull/231
|
||||||
|
|
||||||
* Bugfix #238: API: Return empty array when listing empty folders
|
* Bugfix #238: API: Return empty array when listing empty folders
|
||||||
|
|
||||||
Rest-server returned `null` when listing an empty folder. This has been changed to returning
|
Rest-server returned `null` when listing an empty folder. This has been changed
|
||||||
an empty array in accordance with the REST protocol specification. This change has no impact on
|
to returning an empty array in accordance with the REST protocol specification.
|
||||||
restic users.
|
This change has no impact on restic users.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/238
|
https://github.com/restic/rest-server/issues/238
|
||||||
https://github.com/restic/rest-server/pull/239
|
https://github.com/restic/rest-server/pull/239
|
||||||
|
|
||||||
* Enhancement #217: Log to stdout using the `--log -` option
|
* Enhancement #217: Log to stdout using the `--log -` option
|
||||||
|
|
||||||
Logging to stdout was possible using `--log /dev/stdout`. However, when the rest server is run
|
Logging to stdout was possible using `--log /dev/stdout`. However, when the rest
|
||||||
as a different user, for example, using
|
server is run as a different user, for example, using
|
||||||
|
|
||||||
`sudo -u restic rest-server [...] --log /dev/stdout`
|
`sudo -u restic rest-server [...] --log /dev/stdout`
|
||||||
|
|
||||||
This did not work due to permission issues.
|
This did not work due to permission issues.
|
||||||
|
|
||||||
For logging to stdout, the `--log` option now supports the special filename `-` which also
|
For logging to stdout, the `--log` option now supports the special filename `-`
|
||||||
works in these cases.
|
which also works in these cases.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/217
|
https://github.com/restic/rest-server/pull/217
|
||||||
|
|
||||||
@@ -69,68 +227,69 @@ Details
|
|||||||
|
|
||||||
* Bugfix #183: Allow usernames containing underscore and more
|
* Bugfix #183: Allow usernames containing underscore and more
|
||||||
|
|
||||||
The security fix in rest-server 0.11.0 (#131) disallowed usernames containing and
|
The security fix in rest-server 0.11.0 (#131) disallowed usernames containing
|
||||||
underscore "_". The list of allowed characters has now been changed to include Unicode
|
and underscore "_". The list of allowed characters has now been changed to
|
||||||
characters, numbers, "_", "-", "." and "@".
|
include Unicode characters, numbers, "_", "-", "." and "@".
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/183
|
https://github.com/restic/rest-server/issues/183
|
||||||
https://github.com/restic/rest-server/pull/184
|
https://github.com/restic/rest-server/pull/184
|
||||||
|
|
||||||
* Bugfix #219: Ignore unexpected files in the data/ folder
|
* Bugfix #219: Ignore unexpected files in the data/ folder
|
||||||
|
|
||||||
If the data folder of a repository contained files, this would prevent restic from retrieving a
|
If the data folder of a repository contained files, this would prevent restic
|
||||||
list of file data files. This has been fixed. As a workaround remove the files that are directly
|
from retrieving a list of file data files. This has been fixed. As a workaround
|
||||||
contained in the data folder (e.g., `.DS_Store` files).
|
remove the files that are directly contained in the data folder (e.g.,
|
||||||
|
`.DS_Store` files).
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/219
|
https://github.com/restic/rest-server/issues/219
|
||||||
https://github.com/restic/rest-server/pull/221
|
https://github.com/restic/rest-server/pull/221
|
||||||
|
|
||||||
* Bugfix #1871: Return 500 "Internal server error" if files cannot be read
|
* Bugfix #1871: Return 500 "Internal server error" if files cannot be read
|
||||||
|
|
||||||
When files in a repository cannot be read by rest-server, for example after running `restic
|
When files in a repository cannot be read by rest-server, for example after
|
||||||
prune` directly on the server hosting the repositories in a way that causes filesystem
|
running `restic prune` directly on the server hosting the repositories in a way
|
||||||
permissions to be wrong, rest-server previously returned 404 "Not Found" as status code. This
|
that causes filesystem permissions to be wrong, rest-server previously returned
|
||||||
was causing confusing for users.
|
404 "Not Found" as status code. This was causing confusing for users.
|
||||||
|
|
||||||
The error handling has now been fixed to only return 404 "Not Found" if the file actually does not
|
The error handling has now been fixed to only return 404 "Not Found" if the file
|
||||||
exist. Otherwise a 500 "Internal server error" is reported to the client and the underlying
|
actually does not exist. Otherwise a 500 "Internal server error" is reported to
|
||||||
error is logged at the server side.
|
the client and the underlying error is logged at the server side.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/1871
|
https://github.com/restic/rest-server/issues/1871
|
||||||
https://github.com/restic/rest-server/pull/195
|
https://github.com/restic/rest-server/pull/195
|
||||||
|
|
||||||
* Change #207: Return error if command-line arguments are specified
|
* Change #207: Return error if command-line arguments are specified
|
||||||
|
|
||||||
Command line arguments are ignored by rest-server, but there was previously no indication of
|
Command line arguments are ignored by rest-server, but there was previously no
|
||||||
this when they were supplied anyway.
|
indication of this when they were supplied anyway.
|
||||||
|
|
||||||
To prevent usage errors an error is now printed when command line arguments are supplied,
|
To prevent usage errors an error is now printed when command line arguments are
|
||||||
instead of them being silently ignored.
|
supplied, instead of them being silently ignored.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/207
|
https://github.com/restic/rest-server/pull/207
|
||||||
|
|
||||||
* Change #208: Update dependencies and require Go 1.17 or newer
|
* Change #208: Update dependencies and require Go 1.17 or newer
|
||||||
|
|
||||||
Most dependencies have been updated. Since some libraries require newer language features,
|
Most dependencies have been updated. Since some libraries require newer language
|
||||||
support for Go 1.15-1.16 has been dropped, which means that rest-server now requires at least
|
features, support for Go 1.15-1.16 has been dropped, which means that
|
||||||
Go 1.17 to build.
|
rest-server now requires at least Go 1.17 to build.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/208
|
https://github.com/restic/rest-server/pull/208
|
||||||
|
|
||||||
* Enhancement #133: Cache basic authentication credentials
|
* Enhancement #133: Cache basic authentication credentials
|
||||||
|
|
||||||
To speed up the verification of basic auth credentials, rest-server now caches passwords for a
|
To speed up the verification of basic auth credentials, rest-server now caches
|
||||||
minute in memory. That way the expensive verification of basic auth credentials can be skipped
|
passwords for a minute in memory. That way the expensive verification of basic
|
||||||
for most requests issued by a single restic run. The password is kept in memory in a hashed form
|
auth credentials can be skipped for most requests issued by a single restic run.
|
||||||
and not as plaintext.
|
The password is kept in memory in a hashed form and not as plaintext.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/133
|
https://github.com/restic/rest-server/issues/133
|
||||||
https://github.com/restic/rest-server/pull/138
|
https://github.com/restic/rest-server/pull/138
|
||||||
|
|
||||||
* Enhancement #187: Allow configurable location for `.htpasswd` file
|
* Enhancement #187: Allow configurable location for `.htpasswd` file
|
||||||
|
|
||||||
It is now possible to specify the location of the `.htpasswd` file using the `--htpasswd-file`
|
It is now possible to specify the location of the `.htpasswd` file using the
|
||||||
option.
|
`--htpasswd-file` option.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/187
|
https://github.com/restic/rest-server/issues/187
|
||||||
https://github.com/restic/rest-server/pull/188
|
https://github.com/restic/rest-server/pull/188
|
||||||
@@ -161,11 +320,12 @@ Details
|
|||||||
|
|
||||||
* Security #131: Prevent loading of usernames containing a slash
|
* Security #131: Prevent loading of usernames containing a slash
|
||||||
|
|
||||||
"/" is valid char in HTTP authorization headers, but is also used in rest-server to map
|
"/" is valid char in HTTP authorization headers, but is also used in rest-server
|
||||||
usernames to private repos.
|
to map usernames to private repos.
|
||||||
|
|
||||||
This commit prevents loading maliciously composed usernames like "/foo/config" by
|
This commit prevents loading maliciously composed usernames like "/foo/config"
|
||||||
restricting the allowed characters to the unicode character class, numbers, "-", "." and "@".
|
by restricting the allowed characters to the unicode character class, numbers,
|
||||||
|
"-", "." and "@".
|
||||||
|
|
||||||
This prevents requests to other users files like:
|
This prevents requests to other users files like:
|
||||||
|
|
||||||
@@ -177,64 +337,71 @@ Details
|
|||||||
|
|
||||||
* Bugfix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION`
|
* Bugfix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION`
|
||||||
|
|
||||||
Rest-server 0.10.0 introduced a regression which caused the `DISABLE_AUTHENTICATION`
|
Rest-server 0.10.0 introduced a regression which caused the
|
||||||
environment variable to stop working for the Docker container. This has been fixed by
|
`DISABLE_AUTHENTICATION` environment variable to stop working for the Docker
|
||||||
automatically setting the option `--no-auth` to disable authentication.
|
container. This has been fixed by automatically setting the option `--no-auth`
|
||||||
|
to disable authentication.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/119
|
https://github.com/restic/rest-server/issues/119
|
||||||
https://github.com/restic/rest-server/pull/124
|
https://github.com/restic/rest-server/pull/124
|
||||||
|
|
||||||
* Bugfix #142: Fix possible data loss due to interrupted network connections
|
* Bugfix #142: Fix possible data loss due to interrupted network connections
|
||||||
|
|
||||||
When rest-server was run without `--append-only` it was possible to lose uploaded files in a
|
When rest-server was run without `--append-only` it was possible to lose
|
||||||
specific scenario in which a network connection was interrupted.
|
uploaded files in a specific scenario in which a network connection was
|
||||||
|
interrupted.
|
||||||
|
|
||||||
For the data loss to occur a file upload by restic would have to be interrupted such that restic
|
For the data loss to occur a file upload by restic would have to be interrupted
|
||||||
notices the interrupted network connection before the rest-server. Then restic would have to
|
such that restic notices the interrupted network connection before the
|
||||||
retry the file upload and finish it before the rest-server notices that the initial upload has
|
rest-server. Then restic would have to retry the file upload and finish it
|
||||||
failed. Then the uploaded file would be accidentally removed by rest-server when trying to
|
before the rest-server notices that the initial upload has failed. Then the
|
||||||
|
uploaded file would be accidentally removed by rest-server when trying to
|
||||||
cleanup the failed upload.
|
cleanup the failed upload.
|
||||||
|
|
||||||
This has been fixed by always uploading to a temporary file first which is moved in position only
|
This has been fixed by always uploading to a temporary file first which is moved
|
||||||
once it was uploaded completely.
|
in position only once it was uploaded completely.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/142
|
https://github.com/restic/rest-server/pull/142
|
||||||
|
|
||||||
* Bugfix #155: Reply "insufficient storage" on disk full or over-quota
|
* Bugfix #155: Reply "insufficient storage" on disk full or over-quota
|
||||||
|
|
||||||
When there was no space left on disk, or any other write-related error occurred, rest-server
|
When there was no space left on disk, or any other write-related error occurred,
|
||||||
replied with HTTP status code 400 (Bad request). This is misleading (restic client will dump
|
rest-server replied with HTTP status code 400 (Bad request). This is misleading
|
||||||
the status code to the user).
|
(restic client will dump the status code to the user).
|
||||||
|
|
||||||
Rest-server now replies with two different status codes in these situations: * HTTP 507
|
Rest-server now replies with two different status codes in these situations: *
|
||||||
"Insufficient storage" is the status on disk full or repository over-quota * HTTP 500
|
HTTP 507 "Insufficient storage" is the status on disk full or repository
|
||||||
"Internal server error" is used for other disk-related errors
|
over-quota * HTTP 500 "Internal server error" is used for other disk-related
|
||||||
|
errors
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/155
|
https://github.com/restic/rest-server/issues/155
|
||||||
https://github.com/restic/rest-server/pull/160
|
https://github.com/restic/rest-server/pull/160
|
||||||
|
|
||||||
* Bugfix #157: Use platform-specific temporary directory as default data directory
|
* Bugfix #157: Use platform-specific temporary directory as default data directory
|
||||||
|
|
||||||
If no data directory is specificed, then rest-server now uses the Go standard library
|
If no data directory is specificed, then rest-server now uses the Go standard
|
||||||
functions to retrieve the standard temporary directory path for the current platform.
|
library functions to retrieve the standard temporary directory path for the
|
||||||
|
current platform.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/157
|
https://github.com/restic/rest-server/issues/157
|
||||||
https://github.com/restic/rest-server/pull/158
|
https://github.com/restic/rest-server/pull/158
|
||||||
|
|
||||||
* Change #112: Add subrepo support and refactor server code
|
* Change #112: Add subrepo support and refactor server code
|
||||||
|
|
||||||
Support for multi-level repositories has been added, so now each user can have its own
|
Support for multi-level repositories has been added, so now each user can have
|
||||||
subrepositories. This feature is always enabled.
|
its own subrepositories. This feature is always enabled.
|
||||||
|
|
||||||
Authentication for the Prometheus /metrics endpoint can now be disabled with the new
|
Authentication for the Prometheus /metrics endpoint can now be disabled with the
|
||||||
`--prometheus-no-auth` flag.
|
new `--prometheus-no-auth` flag.
|
||||||
|
|
||||||
We have split out all HTTP handling to a separate `repo` subpackage to cleanly separate the
|
We have split out all HTTP handling to a separate `repo` subpackage to cleanly
|
||||||
server code from the code that handles a single repository. The new RepoHandler also makes it
|
separate the server code from the code that handles a single repository. The new
|
||||||
easier to reuse rest-server as a Go component in any other HTTP server.
|
RepoHandler also makes it easier to reuse rest-server as a Go component in any
|
||||||
|
other HTTP server.
|
||||||
|
|
||||||
The refactoring makes the code significantly easier to follow and understand, which in turn
|
The refactoring makes the code significantly easier to follow and understand,
|
||||||
makes it easier to add new features, audit for security and debug issues.
|
which in turn makes it easier to add new features, audit for security and debug
|
||||||
|
issues.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/109
|
https://github.com/restic/rest-server/issues/109
|
||||||
https://github.com/restic/rest-server/issues/107
|
https://github.com/restic/rest-server/issues/107
|
||||||
@@ -242,27 +409,28 @@ Details
|
|||||||
|
|
||||||
* Change #146: Build rest-server at docker container build time
|
* Change #146: Build rest-server at docker container build time
|
||||||
|
|
||||||
The Dockerfile now includes a build stage such that the latest rest-server is always built and
|
The Dockerfile now includes a build stage such that the latest rest-server is
|
||||||
packaged. This is done in a standard golang container to ensure a clean build environment and
|
always built and packaged. This is done in a standard golang container to ensure
|
||||||
only the final binary is shipped rather than the whole build environment.
|
a clean build environment and only the final binary is shipped rather than the
|
||||||
|
whole build environment.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/146
|
https://github.com/restic/rest-server/issues/146
|
||||||
https://github.com/restic/rest-server/pull/145
|
https://github.com/restic/rest-server/pull/145
|
||||||
|
|
||||||
* Enhancement #122: Verify uploaded files
|
* Enhancement #122: Verify uploaded files
|
||||||
|
|
||||||
The rest-server now by default verifies that the hash of content of uploaded files matches
|
The rest-server now by default verifies that the hash of content of uploaded
|
||||||
their filename. This ensures that transmission errors are detected and forces restic to retry
|
files matches their filename. This ensures that transmission errors are detected
|
||||||
the upload. On low-power devices it can make sense to disable this check by passing the
|
and forces restic to retry the upload. On low-power devices it can make sense to
|
||||||
`--no-verify-upload` flag.
|
disable this check by passing the `--no-verify-upload` flag.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/122
|
https://github.com/restic/rest-server/issues/122
|
||||||
https://github.com/restic/rest-server/pull/130
|
https://github.com/restic/rest-server/pull/130
|
||||||
|
|
||||||
* Enhancement #126: Allow running rest-server via systemd socket activation
|
* Enhancement #126: Allow running rest-server via systemd socket activation
|
||||||
|
|
||||||
We've added the option to have systemd create the listening socket and start the rest-server on
|
We've added the option to have systemd create the listening socket and start the
|
||||||
demand.
|
rest-server on demand.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/126
|
https://github.com/restic/rest-server/issues/126
|
||||||
https://github.com/restic/rest-server/pull/151
|
https://github.com/restic/rest-server/pull/151
|
||||||
@@ -270,9 +438,9 @@ Details
|
|||||||
|
|
||||||
* Enhancement #148: Expand use of security features in example systemd unit file
|
* Enhancement #148: Expand use of security features in example systemd unit file
|
||||||
|
|
||||||
The example systemd unit file now enables additional systemd features to mitigate potential
|
The example systemd unit file now enables additional systemd features to
|
||||||
security vulnerabilities in rest-server and the various packages and operating system
|
mitigate potential security vulnerabilities in rest-server and the various
|
||||||
components which it relies upon.
|
packages and operating system components which it relies upon.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/148
|
https://github.com/restic/rest-server/issues/148
|
||||||
https://github.com/restic/rest-server/pull/149
|
https://github.com/restic/rest-server/pull/149
|
||||||
@@ -298,49 +466,53 @@ Details
|
|||||||
|
|
||||||
* Security #60: Require auth by default, add --no-auth flag
|
* Security #60: Require auth by default, add --no-auth flag
|
||||||
|
|
||||||
In order to prevent users from accidentally exposing rest-server without authentication,
|
In order to prevent users from accidentally exposing rest-server without
|
||||||
rest-server now defaults to requiring a .htpasswd. If you want to disable authentication, you
|
authentication, rest-server now defaults to requiring a .htpasswd. If you want
|
||||||
need to explicitly pass the new --no-auth flag.
|
to disable authentication, you need to explicitly pass the new --no-auth flag.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/60
|
https://github.com/restic/rest-server/issues/60
|
||||||
https://github.com/restic/rest-server/pull/61
|
https://github.com/restic/rest-server/pull/61
|
||||||
|
|
||||||
* Security #64: Refuse overwriting config file in append-only mode
|
* Security #64: Refuse overwriting config file in append-only mode
|
||||||
|
|
||||||
While working on the `rclone serve restic` command we noticed that is currently possible to
|
While working on the `rclone serve restic` command we noticed that is currently
|
||||||
overwrite the config file in a repo even if `--append-only` is specified. The first commit adds
|
possible to overwrite the config file in a repo even if `--append-only` is
|
||||||
proper tests, and the second commit fixes the issue.
|
specified. The first commit adds proper tests, and the second commit fixes the
|
||||||
|
issue.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/64
|
https://github.com/restic/rest-server/pull/64
|
||||||
|
|
||||||
* Security #117: Stricter path sanitization
|
* Security #117: Stricter path sanitization
|
||||||
|
|
||||||
The framework we're using in rest-server to decode paths to repositories allowed specifying
|
The framework we're using in rest-server to decode paths to repositories allowed
|
||||||
URL-encoded characters in paths, including sensitive characters such as `/` (encoded as
|
specifying URL-encoded characters in paths, including sensitive characters such
|
||||||
`%2F`).
|
as `/` (encoded as `%2F`).
|
||||||
|
|
||||||
We've changed this unintended behavior, such that rest-server now rejects such paths. In
|
We've changed this unintended behavior, such that rest-server now rejects such
|
||||||
particular, it is no longer possible to specify sub-repositories for users by encoding the
|
paths. In particular, it is no longer possible to specify sub-repositories for
|
||||||
path with `%2F`, such as `http://localhost:8000/foo%2Fbar`, which means that this will
|
users by encoding the path with `%2F`, such as
|
||||||
unfortunately be a breaking change in that case.
|
`http://localhost:8000/foo%2Fbar`, which means that this will unfortunately be a
|
||||||
|
breaking change in that case.
|
||||||
|
|
||||||
If using sub-repositories for users is important to you, please let us know in the forum, so we
|
If using sub-repositories for users is important to you, please let us know in
|
||||||
can learn about your use case and implement this properly. As it currently stands, the ability
|
the forum, so we can learn about your use case and implement this properly. As
|
||||||
to use sub-repositories was an unintentional feature made possible by the URL decoding
|
it currently stands, the ability to use sub-repositories was an unintentional
|
||||||
framework used, and hence never meant to be supported in the first place. If we wish to have this
|
feature made possible by the URL decoding framework used, and hence never meant
|
||||||
feature in rest-server, we'd like to have it implemented properly and intentionally.
|
to be supported in the first place. If we wish to have this feature in
|
||||||
|
rest-server, we'd like to have it implemented properly and intentionally.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/117
|
https://github.com/restic/rest-server/issues/117
|
||||||
|
|
||||||
* Change #102: Remove vendored dependencies
|
* Change #102: Remove vendored dependencies
|
||||||
|
|
||||||
We've removed the vendored dependencies (in the subdir `vendor/`) similar to what we did for
|
We've removed the vendored dependencies (in the subdir `vendor/`) similar to
|
||||||
`restic` itself. When building restic, the Go compiler automatically fetches the
|
what we did for `restic` itself. When building restic, the Go compiler
|
||||||
dependencies. It will also cryptographically verify that the correct code has been fetched by
|
automatically fetches the dependencies. It will also cryptographically verify
|
||||||
using the hashes in `go.sum` (see the link to the documentation below).
|
that the correct code has been fetched by using the hashes in `go.sum` (see the
|
||||||
|
link to the documentation below).
|
||||||
|
|
||||||
Building the rest-server now requires Go 1.11 or newer, since we're using Go Modules for
|
Building the rest-server now requires Go 1.11 or newer, since we're using Go
|
||||||
dependency management. Older Go versions are not supported any more.
|
Modules for dependency management. Older Go versions are not supported any more.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/102
|
https://github.com/restic/rest-server/issues/102
|
||||||
https://golang.org/cmd/go/#hdr-Module_downloading_and_verification
|
https://golang.org/cmd/go/#hdr-Module_downloading_and_verification
|
||||||
|
|||||||
57
README.md
57
README.md
@@ -11,7 +11,7 @@ Rest Server is a high performance HTTP server that implements restic's [REST bac
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Rest Server requires Go 1.17 or higher to build. The only tested compiler is the official Go compiler. Building server with `gccgo` may work, but is not supported.
|
Rest Server requires Go 1.23 or higher to build. The only tested compiler is the official Go compiler.
|
||||||
|
|
||||||
The required version of restic backup client to use with `rest-server` is [v0.7.1](https://github.com/restic/restic/releases/tag/v0.7.1) or higher.
|
The required version of restic backup client to use with `rest-server` is [v0.7.1](https://github.com/restic/restic/releases/tag/v0.7.1) or higher.
|
||||||
|
|
||||||
@@ -32,24 +32,27 @@ Usage:
|
|||||||
rest-server [flags]
|
rest-server [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
--append-only enable append only mode
|
--append-only enable append only mode
|
||||||
--cpu-profile string write CPU profile to file
|
--cpu-profile string write CPU profile to file
|
||||||
--debug output debug messages
|
--debug output debug messages
|
||||||
-h, --help help for rest-server
|
--group-accessible-repos let filesystem group be able to access repo files
|
||||||
--htpasswd-file string location of .htpasswd file (default: "<data directory>/.htpasswd")
|
-h, --help help for rest-server
|
||||||
--listen string listen address (default ":8000")
|
--htpasswd-file string location of .htpasswd file (default: "<data directory>/.htpasswd)"
|
||||||
--log filename write HTTP requests in the combined log format to the specified filename
|
--listen string listen address (default ":8000")
|
||||||
--max-size int the maximum size of the repository in bytes
|
--log filename write HTTP requests in the combined log format to the specified filename (use "-" for logging to stdout)
|
||||||
--no-auth disable .htpasswd authentication
|
--max-size int the maximum size of the repository in bytes
|
||||||
--no-verify-upload do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device
|
--no-auth disable .htpasswd authentication
|
||||||
--path string data directory (default "/tmp/restic")
|
--no-verify-upload do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device
|
||||||
--private-repos users can only access their private repo
|
--path string data directory (default "/tmp/restic")
|
||||||
--prometheus enable Prometheus metrics
|
--private-repos users can only access their private repo
|
||||||
--prometheus-no-auth disable auth for Prometheus /metrics endpoint
|
--prometheus enable Prometheus metrics
|
||||||
--tls turn on TLS support
|
--prometheus-no-auth disable auth for Prometheus /metrics endpoint
|
||||||
--tls-cert string TLS certificate path
|
--proxy-auth-username string specifies the HTTP header containing the username for proxy-based authentication
|
||||||
--tls-key string TLS key path
|
--tls turn on TLS support
|
||||||
-v, --version version for rest-server
|
--tls-cert string TLS certificate path
|
||||||
|
--tls-key string TLS key path
|
||||||
|
--tls-min-ver string TLS min version, one of (1.2|1.3) (default "1.2")
|
||||||
|
-v, --version version for rest-server
|
||||||
```
|
```
|
||||||
|
|
||||||
By default the server persists backup data in the OS temporary directory (`/tmp/restic` on Linux/BSD and others, in `%TEMP%\\restic` in Windows, etc). **If `rest-server` is launched using the default path, all backups will be lost**. To start the server with a custom persistence directory and with authentication disabled:
|
By default the server persists backup data in the OS temporary directory (`/tmp/restic` on Linux/BSD and others, in `%TEMP%\\restic` in Windows, etc). **If `rest-server` is launched using the default path, all backups will be lost**. To start the server with a custom persistence directory and with authentication disabled:
|
||||||
@@ -68,7 +71,7 @@ If you want to disable authentication, you must add the `--no-auth` flag. If thi
|
|||||||
|
|
||||||
NOTE: In older versions of rest-server (up to 0.9.7), this flag does not exist and the server disables authentication if `.htpasswd` is missing or cannot be opened.
|
NOTE: In older versions of rest-server (up to 0.9.7), this flag does not exist and the server disables authentication if `.htpasswd` is missing or cannot be opened.
|
||||||
|
|
||||||
By default the server uses HTTP protocol. This is not very secure since with Basic Authentication, user name and passwords will be sent in clear text in every request. In order to enable TLS support just add the `--tls` argument and add a private and public key at the root of your persistence directory. You may also specify private and public keys by `--tls-cert` and `--tls-key`.
|
By default the server uses HTTP protocol. This is not very secure since with Basic Authentication, user name and passwords will be sent in clear text in every request. In order to enable TLS support just add the `--tls` argument and add a private and public key at the root of your persistence directory. You may also specify private and public keys by `--tls-cert` and `--tls-key` and set the minimum TLS version to 1.3 using `--tls-min-ver 1.3`.
|
||||||
|
|
||||||
Signed certificate is normally required by the restic backend, but if you just want to test the feature you can generate password-less unsigned keys with the following command:
|
Signed certificate is normally required by the restic backend, but if you just want to test the feature you can generate password-less unsigned keys with the following command:
|
||||||
|
|
||||||
@@ -139,6 +142,16 @@ docker exec -it rest_server create_user myuser mypassword
|
|||||||
docker exec -it rest_server delete_user myuser
|
docker exec -it rest_server delete_user myuser
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Proxy Authentication
|
||||||
|
|
||||||
|
See above for no authentication (`--no-auth`) and basic authentication.
|
||||||
|
|
||||||
|
To delegate authentication to a proxy, use the `--proxy-auth-username` flag. The specified header name, for example `X-Forwarded-User`,
|
||||||
|
must be present in the request headers and specifies the username. Basic authentication is disabled when this flag is set.
|
||||||
|
|
||||||
|
Warning: rest-server trusts the username in the header. It is the responsibility of the proxy
|
||||||
|
to ensure that the username is correct and cannot be forged by an attacker.
|
||||||
|
|
||||||
|
|
||||||
## Prometheus support and Grafana dashboard
|
## Prometheus support and Grafana dashboard
|
||||||
|
|
||||||
@@ -147,6 +160,10 @@ The server can be started with `--prometheus` to expose [Prometheus](https://pro
|
|||||||
This repository contains an example full stack Docker Compose setup with a Grafana dashboard in [examples/compose-with-grafana/](examples/compose-with-grafana/).
|
This repository contains an example full stack Docker Compose setup with a Grafana dashboard in [examples/compose-with-grafana/](examples/compose-with-grafana/).
|
||||||
|
|
||||||
|
|
||||||
|
## Group-accessible Repositories
|
||||||
|
|
||||||
|
Rest-server supports making repositories accessible to the filesystem group by setting the `--group-accessible-repos` option. Note that permissions of existing files are not modified. To allow the group to read and write file, use a umask of `007`. To only grant read access use `027`. To make an existing repository group-accessible, use `chmod -R g+rwX /path/to/repo`.
|
||||||
|
|
||||||
## Why use Rest Server?
|
## Why use Rest Server?
|
||||||
|
|
||||||
Compared to the SFTP backend, the REST backend has better performance, especially so if you can skip additional crypto overhead by using plain HTTP transport (restic already properly encrypts all data it sends, so using HTTPS is mostly about authentication).
|
Compared to the SFTP backend, the REST backend has better performance, especially so if you can skip additional crypto overhead by using plain HTTP transport (restic already properly encrypts all data it sends, so using HTTPS is mostly about authentication).
|
||||||
|
|||||||
@@ -30,11 +30,11 @@
|
|||||||
git tag -a -s -m "v${VERSION}" "v${VERSION}"
|
git tag -a -s -m "v${VERSION}" "v${VERSION}"
|
||||||
git push --tags
|
git push --tags
|
||||||
|
|
||||||
6. Build the project (use `--skip-publish` for testing, or pass `--config` to
|
6. Build the project (use `--snapshot` for testing, or pass `--config` to
|
||||||
use another config file):
|
use another config file):
|
||||||
|
|
||||||
goreleaser \
|
goreleaser \
|
||||||
release \
|
release --parallelism 4 \
|
||||||
--release-notes <(calens --template changelog/CHANGELOG-GitHub.tmpl --version "${VERSION}")
|
--release-notes <(calens --template changelog/CHANGELOG-GitHub.tmpl --version "${VERSION}")
|
||||||
|
|
||||||
7. Set a new version in `main.go` and commit the result:
|
7. Set a new version in `main.go` and commit the result:
|
||||||
|
|||||||
2
build.go
2
build.go
@@ -58,7 +58,7 @@ var config = Config{
|
|||||||
Namespace: "github.com/restic/rest-server", // subdir of GOPATH, e.g. "github.com/foo/bar"
|
Namespace: "github.com/restic/rest-server", // subdir of GOPATH, e.g. "github.com/foo/bar"
|
||||||
Main: "github.com/restic/rest-server/cmd/rest-server", // package name for the main package
|
Main: "github.com/restic/rest-server/cmd/rest-server", // package name for the main package
|
||||||
Tests: []string{"./..."}, // tests to run
|
Tests: []string{"./..."}, // tests to run
|
||||||
MinVersion: GoVersion{Major: 1, Minor: 15, Patch: 0}, // minimum Go version supported
|
MinVersion: GoVersion{Major: 1, Minor: 23, Patch: 0}, // minimum Go version supported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config configures the build.
|
// Config configures the build.
|
||||||
|
|||||||
7
changelog/0.13.0_2024-07-26/pull-267
Normal file
7
changelog/0.13.0_2024-07-26/pull-267
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Change: Update dependencies and require Go 1.18 or newer
|
||||||
|
|
||||||
|
Most dependencies have been updated. Since some libraries require newer language
|
||||||
|
features, support for Go 1.17 has been dropped, which means that rest-server
|
||||||
|
now requires at least Go 1.18 to build.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/267
|
||||||
9
changelog/0.13.0_2024-07-26/pull-271
Normal file
9
changelog/0.13.0_2024-07-26/pull-271
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Enhancement: Print listening address after start-up
|
||||||
|
|
||||||
|
When started with `--listen :0`, rest-server would print `start server on :0`
|
||||||
|
|
||||||
|
The message now also includes the actual address listened on, for example
|
||||||
|
`start server on 0.0.0.0:37333`. This is useful when starting a server with
|
||||||
|
an auto-allocated free port number (port 0).
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/271
|
||||||
15
changelog/0.13.0_2024-07-26/pull-272
Normal file
15
changelog/0.13.0_2024-07-26/pull-272
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Enhancement: Support listening on a unix socket
|
||||||
|
|
||||||
|
It is now possible to make rest-server listen on a unix socket by prefixing
|
||||||
|
the socket filename with `unix:` and passing it to the `--listen` option,
|
||||||
|
for example `--listen unix:/tmp/foo`.
|
||||||
|
|
||||||
|
This is useful in combination with remote port forwarding to enable a remote
|
||||||
|
server to backup locally, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
rest-server --listen unix:/tmp/foo &
|
||||||
|
ssh -R /tmp/foo:/tmp/foo user@host restic -r rest:http+unix:///tmp/foo:/repo backup
|
||||||
|
```
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/272
|
||||||
9
changelog/0.13.0_2024-07-26/pull-273
Normal file
9
changelog/0.13.0_2024-07-26/pull-273
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Change: Shut down cleanly on TERM and INT signals
|
||||||
|
|
||||||
|
Rest-server now listens for TERM and INT signals and cleanly closes down the
|
||||||
|
http.Server and listener when receiving either of them.
|
||||||
|
|
||||||
|
This is particularly useful when listening on a unix socket, as the server
|
||||||
|
will now remove the socket file when it shuts down.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/273
|
||||||
10
changelog/0.14.0_2025-05-31/issue-189
Normal file
10
changelog/0.14.0_2025-05-31/issue-189
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Enhancement: Support group accessible repositories
|
||||||
|
|
||||||
|
Rest-server now supports making repositories accessible to the filesystem group
|
||||||
|
by setting the `--group-accessible-repos` option. Note that permissions of
|
||||||
|
existing files are not modified. To allow the group to read and write file,
|
||||||
|
use a umask of `007`. To only grant read access use `027`. To make an existing
|
||||||
|
repository group-accessible, use `chmod -R g+rwX /path/to/repo`.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/189
|
||||||
|
https://github.com/restic/rest-server/pull/308
|
||||||
13
changelog/0.14.0_2025-05-31/issue-318
Normal file
13
changelog/0.14.0_2025-05-31/issue-318
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Security: Fix world-readable permissions on new `.htpasswd` files
|
||||||
|
|
||||||
|
On startup the rest-server Docker container creates an empty `.htpasswd` file
|
||||||
|
if none exists yet. This file was world-readable by default, which can be
|
||||||
|
a security risk, even though the file only contains hashed passwords.
|
||||||
|
|
||||||
|
This has been fixed such that new `.htpasswd` files are no longer world-readabble.
|
||||||
|
|
||||||
|
The permissions of existing `.htpasswd` files must be manually changed if
|
||||||
|
relevant in your setup.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/318
|
||||||
|
https://github.com/restic/rest-server/pull/340
|
||||||
7
changelog/0.14.0_2025-05-31/issue-321
Normal file
7
changelog/0.14.0_2025-05-31/issue-321
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Enhancement: Add zip archive format for Windows releases
|
||||||
|
|
||||||
|
Windows users can now download rest-server binaries in zip archive format (.zip)
|
||||||
|
in addition to the existing tar.gz archives.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/321
|
||||||
|
https://github.com/restic/rest-server/pull/346
|
||||||
5
changelog/0.14.0_2025-05-31/pull-295
Normal file
5
changelog/0.14.0_2025-05-31/pull-295
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Enhancement: Output status of append-only mode on startup
|
||||||
|
|
||||||
|
Rest-server now displays the status of append-only mode during startup.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/295
|
||||||
12
changelog/0.14.0_2025-05-31/pull-307
Normal file
12
changelog/0.14.0_2025-05-31/pull-307
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Enhancement: Support proxy-based authentication
|
||||||
|
|
||||||
|
Rest-server now supports authentication via HTTP proxy headers. This feature can
|
||||||
|
be enabled by specifying the username header using the `--proxy-auth-username`
|
||||||
|
option (e.g., `--proxy-auth-username=X-Forwarded-User`).
|
||||||
|
|
||||||
|
When enabled, the server authenticates users based on the specified header and
|
||||||
|
disables Basic Auth. Note that proxy authentication is disabled when `--no-auth`
|
||||||
|
is set.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/174
|
||||||
|
https://github.com/restic/rest-server/pull/307
|
||||||
7
changelog/0.14.0_2025-05-31/pull-315
Normal file
7
changelog/0.14.0_2025-05-31/pull-315
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Enhancement: Hardened tls settings
|
||||||
|
|
||||||
|
Rest-server now uses a secure TLS cipher suite set by default. The minimum TLS
|
||||||
|
version is now TLS 1.2 and can be further increased using the new `--tls-min-ver`
|
||||||
|
option, allowing users to enforce stricter security requirements.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/315
|
||||||
11
changelog/0.14.0_2025-05-31/pull-322
Normal file
11
changelog/0.14.0_2025-05-31/pull-322
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Change: Update dependencies and require Go 1.23 or newer
|
||||||
|
|
||||||
|
All dependencies have been updated. Rest-server now requires Go 1.23 or newer
|
||||||
|
to build.
|
||||||
|
|
||||||
|
This also disables support for TLS versions older than TLS 1.2. On Windows,
|
||||||
|
rest-server now requires at least Windows 10 or Windows Server 2016. On macOS,
|
||||||
|
rest-server now requires at least macOS 11 Big Sur.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/322
|
||||||
|
https://github.com/restic/rest-server/pull/338
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/go-systemd/v22/activation"
|
"github.com/coreos/go-systemd/v22/activation"
|
||||||
)
|
)
|
||||||
@@ -23,12 +24,23 @@ func findListener(addr string) (listener net.Listener, err error) {
|
|||||||
switch len(listeners) {
|
switch len(listeners) {
|
||||||
case 0:
|
case 0:
|
||||||
// no listeners found, listen manually
|
// no listeners found, listen manually
|
||||||
listener, err = net.Listen("tcp", addr)
|
if strings.HasPrefix(addr, "unix:") { // if we want to listen on a unix socket
|
||||||
if err != nil {
|
unixAddr, err := net.ResolveUnixAddr("unix", strings.TrimPrefix(addr, "unix:"))
|
||||||
return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to understand unix address %s: %w", addr, err)
|
||||||
|
}
|
||||||
|
listener, err = net.ListenUnix("unix", unixAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
|
||||||
|
}
|
||||||
|
} else { // assume tcp
|
||||||
|
listener, err = net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("start server on %v", addr)
|
log.Printf("start server on %v", listener.Addr())
|
||||||
return listener, nil
|
return listener, nil
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
|
|||||||
78
cmd/rest-server/listener_unix_test.go
Normal file
78
cmd/rest-server/listener_unix_test.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnixSocket(t *testing.T) {
|
||||||
|
td := t.TempDir()
|
||||||
|
|
||||||
|
// this is the socket we'll listen on and connect to
|
||||||
|
tempSocket := filepath.Join(td, "sock")
|
||||||
|
|
||||||
|
// create some content and parent dirs
|
||||||
|
if err := os.MkdirAll(filepath.Join(td, "data", "repo1"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(td, "data", "repo1", "config"), []byte("foo"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the following twice, to test that the server will
|
||||||
|
// cleanup its socket file when quitting, which won't happen
|
||||||
|
// if it doesn't exit gracefully
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
err := testServerWithArgs([]string{
|
||||||
|
"--no-auth",
|
||||||
|
"--path", filepath.Join(td, "data"),
|
||||||
|
"--listen", fmt.Sprintf("unix:%s", tempSocket),
|
||||||
|
}, time.Second, func(ctx context.Context, _ *restServerApp) error {
|
||||||
|
// custom client that will talk HTTP to unix socket
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", tempSocket)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range []struct {
|
||||||
|
Path string
|
||||||
|
StatusCode int
|
||||||
|
}{
|
||||||
|
{"/repo1/", http.StatusMethodNotAllowed},
|
||||||
|
{"/repo1/config", http.StatusOK},
|
||||||
|
{"/repo2/config", http.StatusNotFound},
|
||||||
|
} {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://ignored"+test.Path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != test.StatusCode {
|
||||||
|
return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,6 @@ func findListener(addr string) (listener net.Listener, err error) {
|
|||||||
return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
|
return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("start server on %v", addr)
|
log.Printf("start server on %v", listener.Addr())
|
||||||
return listener, nil
|
return listener, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,159 +1,240 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
restserver "github.com/restic/rest-server"
|
restserver "github.com/restic/rest-server"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type restServerApp struct {
|
||||||
|
CmdRoot *cobra.Command
|
||||||
|
Server restserver.Server
|
||||||
|
CPUProfile string
|
||||||
|
|
||||||
|
listenerAddressMu sync.Mutex
|
||||||
|
listenerAddress net.Addr // set after startup
|
||||||
|
}
|
||||||
|
|
||||||
// cmdRoot is the base command when no other command has been specified.
|
// cmdRoot is the base command when no other command has been specified.
|
||||||
var cmdRoot = &cobra.Command{
|
func newRestServerApp() *restServerApp {
|
||||||
Use: "rest-server",
|
rv := &restServerApp{
|
||||||
Short: "Run a REST server for use with restic",
|
CmdRoot: &cobra.Command{
|
||||||
SilenceErrors: true,
|
Use: "rest-server",
|
||||||
SilenceUsage: true,
|
Short: "Run a REST server for use with restic",
|
||||||
RunE: runRoot,
|
SilenceErrors: true,
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
SilenceUsage: true,
|
||||||
if len(args) != 0 {
|
Args: func(_ *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("rest-server expects no arguments - unknown argument: %s", args[0])
|
if len(args) != 0 {
|
||||||
}
|
return fmt.Errorf("rest-server expects no arguments - unknown argument: %s", args[0])
|
||||||
return nil
|
}
|
||||||
},
|
return nil
|
||||||
Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
},
|
||||||
}
|
Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
||||||
|
},
|
||||||
|
Server: restserver.Server{
|
||||||
|
Path: filepath.Join(os.TempDir(), "restic"),
|
||||||
|
Listen: ":8000",
|
||||||
|
TLSMinVer: "1.2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rv.CmdRoot.RunE = rv.runRoot
|
||||||
|
flags := rv.CmdRoot.Flags()
|
||||||
|
|
||||||
var server = restserver.Server{
|
flags.StringVar(&rv.CPUProfile, "cpu-profile", rv.CPUProfile, "write CPU profile to file")
|
||||||
Path: filepath.Join(os.TempDir(), "restic"),
|
flags.BoolVar(&rv.Server.Debug, "debug", rv.Server.Debug, "output debug messages")
|
||||||
Listen: ":8000",
|
flags.StringVar(&rv.Server.Listen, "listen", rv.Server.Listen, "listen address")
|
||||||
}
|
flags.StringVar(&rv.Server.Log, "log", rv.Server.Log, "write HTTP requests in the combined log format to the specified `filename` (use \"-\" for logging to stdout)")
|
||||||
|
flags.Int64Var(&rv.Server.MaxRepoSize, "max-size", rv.Server.MaxRepoSize, "the maximum size of the repository in bytes")
|
||||||
var (
|
flags.StringVar(&rv.Server.Path, "path", rv.Server.Path, "data directory")
|
||||||
cpuProfile string
|
flags.BoolVar(&rv.Server.TLS, "tls", rv.Server.TLS, "turn on TLS support")
|
||||||
)
|
flags.StringVar(&rv.Server.TLSCert, "tls-cert", rv.Server.TLSCert, "TLS certificate path")
|
||||||
|
flags.StringVar(&rv.Server.TLSKey, "tls-key", rv.Server.TLSKey, "TLS key path")
|
||||||
func init() {
|
flags.StringVar(&rv.Server.TLSMinVer, "tls-min-ver", rv.Server.TLSMinVer, "TLS min version, one of (1.2|1.3)")
|
||||||
flags := cmdRoot.Flags()
|
flags.BoolVar(&rv.Server.NoAuth, "no-auth", rv.Server.NoAuth, "disable authentication")
|
||||||
flags.StringVar(&cpuProfile, "cpu-profile", cpuProfile, "write CPU profile to file")
|
flags.StringVar(&rv.Server.HtpasswdPath, "htpasswd-file", rv.Server.HtpasswdPath, "location of .htpasswd file (default: \"<data directory>/.htpasswd)\"")
|
||||||
flags.BoolVar(&server.Debug, "debug", server.Debug, "output debug messages")
|
flags.StringVar(&rv.Server.ProxyAuthUsername, "proxy-auth-username", rv.Server.ProxyAuthUsername, "specifies the HTTP header containing the username for proxy-based authentication")
|
||||||
flags.StringVar(&server.Listen, "listen", server.Listen, "listen address")
|
flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload,
|
||||||
flags.StringVar(&server.Log, "log", server.Log, "write HTTP requests in the combined log format to the specified `filename` (use \"-\" for logging to stdout)")
|
|
||||||
flags.Int64Var(&server.MaxRepoSize, "max-size", server.MaxRepoSize, "the maximum size of the repository in bytes")
|
|
||||||
flags.StringVar(&server.Path, "path", server.Path, "data directory")
|
|
||||||
flags.BoolVar(&server.TLS, "tls", server.TLS, "turn on TLS support")
|
|
||||||
flags.StringVar(&server.TLSCert, "tls-cert", server.TLSCert, "TLS certificate path")
|
|
||||||
flags.StringVar(&server.TLSKey, "tls-key", server.TLSKey, "TLS key path")
|
|
||||||
flags.BoolVar(&server.NoAuth, "no-auth", server.NoAuth, "disable .htpasswd authentication")
|
|
||||||
flags.StringVar(&server.HtpasswdPath, "htpasswd-file", server.HtpasswdPath, "location of .htpasswd file (default: \"<data directory>/.htpasswd)\"")
|
|
||||||
flags.BoolVar(&server.NoVerifyUpload, "no-verify-upload", server.NoVerifyUpload,
|
|
||||||
"do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device")
|
"do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device")
|
||||||
flags.BoolVar(&server.AppendOnly, "append-only", server.AppendOnly, "enable append only mode")
|
flags.BoolVar(&rv.Server.AppendOnly, "append-only", rv.Server.AppendOnly, "enable append only mode")
|
||||||
flags.BoolVar(&server.PrivateRepos, "private-repos", server.PrivateRepos, "users can only access their private repo")
|
flags.BoolVar(&rv.Server.PrivateRepos, "private-repos", rv.Server.PrivateRepos, "users can only access their private repo")
|
||||||
flags.BoolVar(&server.Prometheus, "prometheus", server.Prometheus, "enable Prometheus metrics")
|
flags.BoolVar(&rv.Server.Prometheus, "prometheus", rv.Server.Prometheus, "enable Prometheus metrics")
|
||||||
flags.BoolVar(&server.PrometheusNoAuth, "prometheus-no-auth", server.PrometheusNoAuth, "disable auth for Prometheus /metrics endpoint")
|
flags.BoolVar(&rv.Server.PrometheusNoAuth, "prometheus-no-auth", rv.Server.PrometheusNoAuth, "disable auth for Prometheus /metrics endpoint")
|
||||||
|
flags.BoolVar(&rv.Server.GroupAccessibleRepos, "group-accessible-repos", rv.Server.GroupAccessibleRepos, "let filesystem group be able to access repo files")
|
||||||
|
|
||||||
|
return rv
|
||||||
}
|
}
|
||||||
|
|
||||||
var version = "0.12.1"
|
var version = "0.14.0-dev"
|
||||||
|
|
||||||
func tlsSettings() (bool, string, string, error) {
|
func (app *restServerApp) tlsSettings() (bool, string, string, error) {
|
||||||
var key, cert string
|
var key, cert string
|
||||||
if !server.TLS && (server.TLSKey != "" || server.TLSCert != "") {
|
if !app.Server.TLS && (app.Server.TLSKey != "" || app.Server.TLSCert != "") {
|
||||||
return false, "", "", errors.New("requires enabled TLS")
|
return false, "", "", errors.New("requires enabled TLS")
|
||||||
} else if !server.TLS {
|
} else if !app.Server.TLS {
|
||||||
return false, "", "", nil
|
return false, "", "", nil
|
||||||
}
|
}
|
||||||
if server.TLSKey != "" {
|
if app.Server.TLSKey != "" {
|
||||||
key = server.TLSKey
|
key = app.Server.TLSKey
|
||||||
} else {
|
} else {
|
||||||
key = filepath.Join(server.Path, "private_key")
|
key = filepath.Join(app.Server.Path, "private_key")
|
||||||
}
|
}
|
||||||
if server.TLSCert != "" {
|
if app.Server.TLSCert != "" {
|
||||||
cert = server.TLSCert
|
cert = app.Server.TLSCert
|
||||||
} else {
|
} else {
|
||||||
cert = filepath.Join(server.Path, "public_key")
|
cert = filepath.Join(app.Server.Path, "public_key")
|
||||||
}
|
}
|
||||||
return server.TLS, key, cert, nil
|
return app.Server.TLS, key, cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRoot(cmd *cobra.Command, args []string) error {
|
// returns the address that the app is listening on.
|
||||||
|
// returns nil if the application hasn't finished starting yet
|
||||||
|
func (app *restServerApp) ListenerAddress() net.Addr {
|
||||||
|
app.listenerAddressMu.Lock()
|
||||||
|
defer app.listenerAddressMu.Unlock()
|
||||||
|
return app.listenerAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *restServerApp) runRoot(_ *cobra.Command, _ []string) error {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
|
|
||||||
log.Printf("Data directory: %s", server.Path)
|
log.Printf("Data directory: %s", app.Server.Path)
|
||||||
|
|
||||||
if cpuProfile != "" {
|
if app.CPUProfile != "" {
|
||||||
f, err := os.Create(cpuProfile)
|
f, err := os.Create(app.CPUProfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
if err := pprof.StartCPUProfile(f); err != nil {
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("CPU profiling enabled")
|
defer pprof.StopCPUProfile()
|
||||||
|
|
||||||
// clean profiling shutdown on sigint
|
log.Println("CPU profiling enabled")
|
||||||
sigintCh := make(chan os.Signal, 1)
|
defer log.Println("Stopped CPU profiling")
|
||||||
go func() {
|
|
||||||
for range sigintCh {
|
|
||||||
pprof.StopCPUProfile()
|
|
||||||
log.Println("Stopped CPU profiling")
|
|
||||||
err := f.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("error closing CPU profile file: %v", err)
|
|
||||||
}
|
|
||||||
os.Exit(130)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
signal.Notify(sigintCh, syscall.SIGINT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if server.NoAuth {
|
if app.Server.NoAuth {
|
||||||
log.Println("Authentication disabled")
|
log.Println("Authentication disabled")
|
||||||
} else {
|
} else {
|
||||||
log.Println("Authentication enabled")
|
if app.Server.ProxyAuthUsername == "" {
|
||||||
|
log.Println("Authentication enabled")
|
||||||
|
} else {
|
||||||
|
log.Println("Proxy Authentication enabled.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler, err := restserver.NewHandler(&server)
|
handler, err := restserver.NewHandler(&app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error: %v", err)
|
log.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if server.PrivateRepos {
|
if app.Server.AppendOnly {
|
||||||
|
log.Println("Append only mode enabled")
|
||||||
|
} else {
|
||||||
|
log.Println("Append only mode disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.Server.PrivateRepos {
|
||||||
log.Println("Private repositories enabled")
|
log.Println("Private repositories enabled")
|
||||||
} else {
|
} else {
|
||||||
log.Println("Private repositories disabled")
|
log.Println("Private repositories disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
enabledTLS, privateKey, publicKey, err := tlsSettings()
|
if app.Server.GroupAccessibleRepos {
|
||||||
|
log.Println("Group accessible repos enabled")
|
||||||
|
} else {
|
||||||
|
log.Println("Group accessible repos disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
enabledTLS, privateKey, publicKey, err := app.tlsSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
listener, err := findListener(server.Listen)
|
listener, err := findListener(app.Server.Listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to listen: %w", err)
|
return fmt.Errorf("unable to listen: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !enabledTLS {
|
// set listener address, this is useful for tests
|
||||||
err = http.Serve(listener, handler)
|
app.listenerAddressMu.Lock()
|
||||||
} else {
|
app.listenerAddress = listener.Addr()
|
||||||
log.Printf("TLS enabled, private key %s, pubkey %v", privateKey, publicKey)
|
app.listenerAddressMu.Unlock()
|
||||||
err = http.ServeTLS(listener, handler, publicKey, privateKey)
|
|
||||||
|
tlscfg := &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
CipherSuites: []uint16{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
switch app.Server.TLSMinVer {
|
||||||
|
case "1.2":
|
||||||
|
tlscfg.MinVersion = tls.VersionTLS12
|
||||||
|
case "1.3":
|
||||||
|
tlscfg.MinVersion = tls.VersionTLS13
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unsupported TLS min version: %s. Allowed versions are 1.2 or 1.3", app.Server.TLSMinVer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
srv := &http.Server{
|
||||||
|
Handler: handler,
|
||||||
|
TLSConfig: tlscfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
// run server in background
|
||||||
|
go func() {
|
||||||
|
if !enabledTLS {
|
||||||
|
err = srv.Serve(listener)
|
||||||
|
} else {
|
||||||
|
log.Printf("TLS enabled, private key %s, pubkey %v", privateKey, publicKey)
|
||||||
|
err = srv.ServeTLS(listener, publicKey, privateKey)
|
||||||
|
}
|
||||||
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalf("listen and serve returned err: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait until done
|
||||||
|
<-app.CmdRoot.Context().Done()
|
||||||
|
|
||||||
|
// gracefully shutdown server
|
||||||
|
if err := srv.Shutdown(context.Background()); err != nil {
|
||||||
|
return fmt.Errorf("server shutdown returned an err: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("shutdown cleanly")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := cmdRoot.Execute(); err != nil {
|
// create context to be notified on interrupt or term signal so that we can shutdown cleanly
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
if err := newRestServerApp().CmdRoot.ExecuteContext(ctx); err != nil {
|
||||||
log.Fatalf("error: %v", err)
|
log.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
restserver "github.com/restic/rest-server"
|
restserver "github.com/restic/rest-server"
|
||||||
)
|
)
|
||||||
@@ -47,17 +54,17 @@ func TestTLSSettings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
app := newRestServerApp()
|
||||||
t.Run("", func(t *testing.T) {
|
t.Run("", func(t *testing.T) {
|
||||||
// defer func() { restserver.Server = defaultConfig }()
|
// defer func() { restserver.Server = defaultConfig }()
|
||||||
if test.passed.Path != "" {
|
if test.passed.Path != "" {
|
||||||
server.Path = test.passed.Path
|
app.Server.Path = test.passed.Path
|
||||||
}
|
}
|
||||||
server.TLS = test.passed.TLS
|
app.Server.TLS = test.passed.TLS
|
||||||
server.TLSKey = test.passed.TLSKey
|
app.Server.TLSKey = test.passed.TLSKey
|
||||||
server.TLSCert = test.passed.TLSCert
|
app.Server.TLSCert = test.passed.TLSCert
|
||||||
|
|
||||||
gotTLS, gotKey, gotCert, err := tlsSettings()
|
gotTLS, gotKey, gotCert, err := app.tlsSettings()
|
||||||
if err != nil && !test.expected.Error {
|
if err != nil && !test.expected.Error {
|
||||||
t.Fatalf("tls_settings returned err (%v)", err)
|
t.Fatalf("tls_settings returned err (%v)", err)
|
||||||
}
|
}
|
||||||
@@ -86,7 +93,7 @@ func TestTLSSettings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetHandler(t *testing.T) {
|
func TestGetHandler(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir("", "rest-server-test")
|
dir, err := os.MkdirTemp("", "rest-server-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -111,8 +118,14 @@ func TestGetHandler(t *testing.T) {
|
|||||||
t.Errorf("NoAuth=true: expected no error, got %v", err)
|
t.Errorf("NoAuth=true: expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With NoAuth = false, no .htpasswd and ProxyAuth = X-Remote-User
|
||||||
|
_, err = getHandler(&restserver.Server{Path: dir, ProxyAuthUsername: "X-Remote-User"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NoAuth=false, ProxyAuthUsername = X-Remote-User: expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// With NoAuth = false and custom .htpasswd
|
// With NoAuth = false and custom .htpasswd
|
||||||
htpFile, err := ioutil.TempFile(dir, "custom")
|
htpFile, err := os.CreateTemp(dir, "custom")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -129,7 +142,7 @@ func TestGetHandler(t *testing.T) {
|
|||||||
|
|
||||||
// Create .htpasswd
|
// Create .htpasswd
|
||||||
htpasswd := filepath.Join(dir, ".htpasswd")
|
htpasswd := filepath.Join(dir, ".htpasswd")
|
||||||
err = ioutil.WriteFile(htpasswd, []byte(""), 0644)
|
err = os.WriteFile(htpasswd, []byte(""), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -146,3 +159,126 @@ func TestGetHandler(t *testing.T) {
|
|||||||
t.Errorf("NoAuth=false with .htpasswd: expected no error, got %v", err)
|
t.Errorf("NoAuth=false with .htpasswd: expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper method to test the app. Starts app with passed arguments,
|
||||||
|
// then will call the callback function which can make requests against
|
||||||
|
// the application. If the callback function fails due to errors returned
|
||||||
|
// by http.Do() (i.e. *url.Error), then it will be retried until successful,
|
||||||
|
// or the passed timeout passes.
|
||||||
|
func testServerWithArgs(args []string, timeout time.Duration, cb func(context.Context, *restServerApp) error) error {
|
||||||
|
// create the app with passed args
|
||||||
|
app := newRestServerApp()
|
||||||
|
app.CmdRoot.SetArgs(args)
|
||||||
|
|
||||||
|
// create context that will timeout
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// wait group for our client and server tasks
|
||||||
|
jobs := &sync.WaitGroup{}
|
||||||
|
jobs.Add(2)
|
||||||
|
|
||||||
|
// run the server, saving the error
|
||||||
|
var serverErr error
|
||||||
|
go func() {
|
||||||
|
defer jobs.Done()
|
||||||
|
defer cancel() // if the server is stopped, no point keep the client alive
|
||||||
|
serverErr = app.CmdRoot.ExecuteContext(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// run the client, saving the error
|
||||||
|
var clientErr error
|
||||||
|
go func() {
|
||||||
|
defer jobs.Done()
|
||||||
|
defer cancel() // once the client is done, stop the server
|
||||||
|
|
||||||
|
var urlError *url.Error
|
||||||
|
|
||||||
|
// execute in loop, as we will retry for network errors
|
||||||
|
// (such as the server hasn't started yet)
|
||||||
|
for {
|
||||||
|
clientErr = cb(ctx, app)
|
||||||
|
switch {
|
||||||
|
case clientErr == nil:
|
||||||
|
return // success, we're done
|
||||||
|
case errors.As(clientErr, &urlError):
|
||||||
|
// if a network error (url.Error), then wait and retry
|
||||||
|
// as server may not be ready yet
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Millisecond * 100):
|
||||||
|
continue
|
||||||
|
case <-ctx.Done(): // unless we run out of time first
|
||||||
|
clientErr = context.Canceled
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return // other error type, we're done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait for both to complete
|
||||||
|
jobs.Wait()
|
||||||
|
|
||||||
|
// report back if either failed
|
||||||
|
if clientErr != nil || serverErr != nil {
|
||||||
|
return fmt.Errorf("client or server error, client: %v, server: %v", clientErr, serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHttpListen(t *testing.T) {
|
||||||
|
td := t.TempDir()
|
||||||
|
|
||||||
|
// create some content and parent dirs
|
||||||
|
if err := os.MkdirAll(filepath.Join(td, "data", "repo1"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(td, "data", "repo1", "config"), []byte("foo"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, args := range [][]string{
|
||||||
|
{"--no-auth", "--path", filepath.Join(td, "data"), "--listen", "127.0.0.1:0"}, // test emphemeral port
|
||||||
|
{"--no-auth", "--path", filepath.Join(td, "data"), "--listen", "127.0.0.1:9000"}, // test "normal" port
|
||||||
|
{"--no-auth", "--path", filepath.Join(td, "data"), "--listen", "127.0.0.1:9000"}, // test that server was shutdown cleanly and that we can re-use that port
|
||||||
|
} {
|
||||||
|
err := testServerWithArgs(args, time.Second*10, func(ctx context.Context, app *restServerApp) error {
|
||||||
|
for _, test := range []struct {
|
||||||
|
Path string
|
||||||
|
StatusCode int
|
||||||
|
}{
|
||||||
|
{"/repo1/", http.StatusMethodNotAllowed},
|
||||||
|
{"/repo1/config", http.StatusOK},
|
||||||
|
{"/repo2/config", http.StatusNotFound},
|
||||||
|
} {
|
||||||
|
listenAddr := app.ListenerAddress()
|
||||||
|
if listenAddr == nil {
|
||||||
|
return &url.Error{} // return this type of err, as we know this will retry
|
||||||
|
}
|
||||||
|
port := strings.Split(listenAddr.String(), ":")[1]
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("http://localhost:%s%s", port, test.Path), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != test.StatusCode {
|
||||||
|
return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ fi
|
|||||||
|
|
||||||
if [ -z "$2" ]; then
|
if [ -z "$2" ]; then
|
||||||
# password from prompt
|
# password from prompt
|
||||||
htpasswd -B $PASSWORD_FILE $1
|
htpasswd -B "$PASSWORD_FILE" "$1"
|
||||||
else
|
else
|
||||||
# read password from command line
|
# read password from command line
|
||||||
htpasswd -B -b $PASSWORD_FILE $1 $2
|
htpasswd -B -b "$PASSWORD_FILE" "$1" "$2"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ if [ -z "$1" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
htpasswd -D $PASSWORD_FILE $1
|
htpasswd -D "$PASSWORD_FILE" "$1"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ if [ -n "$DISABLE_AUTHENTICATION" ]; then
|
|||||||
OPTIONS="--no-auth $OPTIONS"
|
OPTIONS="--no-auth $OPTIONS"
|
||||||
else
|
else
|
||||||
if [ ! -f "$PASSWORD_FILE" ]; then
|
if [ ! -f "$PASSWORD_FILE" ]; then
|
||||||
touch "$PASSWORD_FILE"
|
( umask 027 && touch "$PASSWORD_FILE" )
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -s "$PASSWORD_FILE" ]; then
|
if [ ! -s "$PASSWORD_FILE" ]; then
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ services:
|
|||||||
- "127.0.0.1:8020:9090"
|
- "127.0.0.1:8020:9090"
|
||||||
volumes:
|
volumes:
|
||||||
- prometheusdata:/prometheus
|
- prometheusdata:/prometheus
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
- ./prometheus:/etc/prometheus:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- restserver
|
- restserver
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
Description=Rest Server
|
Description=Rest Server
|
||||||
After=syslog.target
|
After=syslog.target
|
||||||
After=network.target
|
After=network.target
|
||||||
|
Requires=rest-server.socket
|
||||||
# if you want to use socket activation, make sure to require the socket here
|
After=rest-server.socket
|
||||||
#Requires=rest-server.socket
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
@@ -27,8 +26,10 @@ RestartSec=5
|
|||||||
# The following line must be customised to your individual requirements.
|
# The following line must be customised to your individual requirements.
|
||||||
ReadWritePaths=/path/to/backups
|
ReadWritePaths=/path/to/backups
|
||||||
|
|
||||||
# Makes created files group-readable, but inaccessible by others
|
# Files in the data repository are only user accessible by default. Default to
|
||||||
UMask=027
|
# `UMask=077` for consistency. To make created files group-readable, set to
|
||||||
|
# `UMask=007` and pass `--group-accessible-repos` to rest-server via `ExecStart`.
|
||||||
|
UMask=077
|
||||||
|
|
||||||
# If your system doesn't support all of the features below (e.g. because of
|
# If your system doesn't support all of the features below (e.g. because of
|
||||||
# the use of an older version of systemd), you may wish to comment-out
|
# the use of an older version of systemd), you may wish to comment-out
|
||||||
@@ -37,6 +38,11 @@ CapabilityBoundingSet=
|
|||||||
LockPersonality=true
|
LockPersonality=true
|
||||||
MemoryDenyWriteExecute=true
|
MemoryDenyWriteExecute=true
|
||||||
NoNewPrivileges=yes
|
NoNewPrivileges=yes
|
||||||
|
|
||||||
|
# As the listen socket is created by systemd via the rest-server.socket unit, it is
|
||||||
|
# no longer necessary for rest-server to have access to the host network namespace.
|
||||||
|
PrivateNetwork=yes
|
||||||
|
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
PrivateDevices=true
|
PrivateDevices=true
|
||||||
PrivateUsers=true
|
PrivateUsers=true
|
||||||
@@ -51,7 +57,7 @@ ProtectProc=invisible
|
|||||||
ProtectHostname=true
|
ProtectHostname=true
|
||||||
RemoveIPC=true
|
RemoveIPC=true
|
||||||
RestrictNamespaces=true
|
RestrictNamespaces=true
|
||||||
RestrictAddressFamilies=AF_INET AF_INET6
|
RestrictAddressFamilies=none
|
||||||
RestrictSUIDSGID=true
|
RestrictSUIDSGID=true
|
||||||
RestrictRealtime=true
|
RestrictRealtime=true
|
||||||
# if your service crashes with "code=killed, status=31/SYS", you probably tried to run linux_i386 (32bit) binary on a amd64 host
|
# if your service crashes with "code=killed, status=31/SYS", you probably tried to run linux_i386 (32bit) binary on a amd64 host
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[Socket]
|
[Socket]
|
||||||
ListenStream = 8080
|
ListenStream = 8000
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy = sockets.target
|
WantedBy = sockets.target
|
||||||
|
|||||||
31
go.mod
31
go.mod
@@ -1,29 +1,28 @@
|
|||||||
module github.com/restic/rest-server
|
module github.com/restic/rest-server
|
||||||
|
|
||||||
go 1.17
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0
|
github.com/coreos/go-systemd/v22 v22.5.0
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/minio/sha256-simd v1.0.1
|
github.com/minio/sha256-simd v1.0.1
|
||||||
github.com/miolini/datacounter v1.0.3
|
github.com/miolini/datacounter v1.0.3
|
||||||
github.com/prometheus/client_golang v1.16.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.9.1
|
||||||
golang.org/x/crypto v0.11.0
|
golang.org/x/crypto v0.38.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/prometheus/client_model v0.4.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.44.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/prometheus/procfs v0.11.0 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
golang.org/x/sys v0.10.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
594
go.sum
594
go.sum
@@ -1,572 +1,56 @@
|
|||||||
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/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/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
|
||||||
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/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=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
|
|
||||||
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
|
||||||
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/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
|
||||||
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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/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/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
|
||||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
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-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-kit/log v0.2.1/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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
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/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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/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=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
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/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.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.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
|
||||||
github.com/google/go-cmp v0.5.9/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/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
|
||||||
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/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/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
|
||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
|
||||||
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/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 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
|
||||||
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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
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/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
|
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
|
||||||
github.com/miolini/datacounter v1.0.3/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA=
|
github.com/miolini/datacounter v1.0.3/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
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/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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
|
||||||
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
|
||||||
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.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
|
||||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
|
||||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
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.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
|
||||||
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/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.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.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
|
||||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
|
||||||
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
|
|
||||||
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
|
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
|
||||||
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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
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=
|
|
||||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
|
||||||
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/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/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
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-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-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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
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-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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
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.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
|
||||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
|
||||||
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-20201207232520-09787c993a3a/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190215142949-d0b11bdaac8a/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-20191001151750-bb3f8db39f24/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-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-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-20201119102817-f84b799fce68/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-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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
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/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-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-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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
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/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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
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-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/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/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=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
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 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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
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/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.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
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=
|
|
||||||
|
|||||||
53
handlers.go
53
handlers.go
@@ -15,23 +15,26 @@ import (
|
|||||||
|
|
||||||
// Server encapsulates the rest-server's settings and repo management logic
|
// Server encapsulates the rest-server's settings and repo management logic
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Path string
|
Path string
|
||||||
HtpasswdPath string
|
HtpasswdPath string
|
||||||
Listen string
|
Listen string
|
||||||
Log string
|
Log string
|
||||||
CPUProfile string
|
CPUProfile string
|
||||||
TLSKey string
|
TLSKey string
|
||||||
TLSCert string
|
TLSCert string
|
||||||
TLS bool
|
TLSMinVer string
|
||||||
NoAuth bool
|
TLS bool
|
||||||
AppendOnly bool
|
NoAuth bool
|
||||||
PrivateRepos bool
|
ProxyAuthUsername string
|
||||||
Prometheus bool
|
AppendOnly bool
|
||||||
PrometheusNoAuth bool
|
PrivateRepos bool
|
||||||
Debug bool
|
Prometheus bool
|
||||||
MaxRepoSize int64
|
PrometheusNoAuth bool
|
||||||
PanicOnError bool
|
Debug bool
|
||||||
NoVerifyUpload bool
|
MaxRepoSize int64
|
||||||
|
PanicOnError bool
|
||||||
|
NoVerifyUpload bool
|
||||||
|
GroupAccessibleRepos bool
|
||||||
|
|
||||||
htpasswdFile *HtpasswdFile
|
htpasswdFile *HtpasswdFile
|
||||||
quotaManager *quota.Manager
|
quotaManager *quota.Manager
|
||||||
@@ -88,12 +91,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Pass the request to the repo.Handler
|
// Pass the request to the repo.Handler
|
||||||
opt := repo.Options{
|
opt := repo.Options{
|
||||||
AppendOnly: s.AppendOnly,
|
AppendOnly: s.AppendOnly,
|
||||||
Debug: s.Debug,
|
Debug: s.Debug,
|
||||||
QuotaManager: s.quotaManager, // may be nil
|
QuotaManager: s.quotaManager, // may be nil
|
||||||
PanicOnError: s.PanicOnError,
|
PanicOnError: s.PanicOnError,
|
||||||
NoVerifyUpload: s.NoVerifyUpload,
|
NoVerifyUpload: s.NoVerifyUpload,
|
||||||
FsyncWarning: &s.fsyncWarning,
|
FsyncWarning: &s.fsyncWarning,
|
||||||
|
GroupAccessible: s.GroupAccessibleRepos,
|
||||||
}
|
}
|
||||||
if s.Prometheus {
|
if s.Prometheus {
|
||||||
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
|
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
|
||||||
@@ -158,7 +162,8 @@ func join(base string, names ...string) (string, error) {
|
|||||||
// splitURLPath splits the URL path into a folderPath of the subrepo, and
|
// splitURLPath splits the URL path into a folderPath of the subrepo, and
|
||||||
// a remainder that can be passed to repo.Handler.
|
// a remainder that can be passed to repo.Handler.
|
||||||
// Example: /foo/bar/locks/0123... will be split into:
|
// Example: /foo/bar/locks/0123... will be split into:
|
||||||
// ["foo", "bar"] and "/locks/0123..."
|
//
|
||||||
|
// ["foo", "bar"] and "/locks/0123..."
|
||||||
func splitURLPath(urlPath string, maxDepth int) (folderPath []string, remainder string) {
|
func splitURLPath(urlPath string, maxDepth int) (folderPath []string, remainder string) {
|
||||||
if !strings.HasPrefix(urlPath, "/") {
|
if !strings.HasPrefix(urlPath, "/") {
|
||||||
// Really should start with "/"
|
// Really should start with "/"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@@ -165,7 +164,7 @@ func createOverwriteDeleteSeq(t testing.TB, path string, data string) []TestRequ
|
|||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string, string, func()) {
|
func createTestHandler(t *testing.T, conf *Server) (http.Handler, string, string, string, func()) {
|
||||||
buf := make([]byte, 32)
|
buf := make([]byte, 32)
|
||||||
_, err := io.ReadFull(rand.Reader, buf)
|
_, err := io.ReadFull(rand.Reader, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -176,7 +175,7 @@ func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string,
|
|||||||
fileID := hex.EncodeToString(dataHash[:])
|
fileID := hex.EncodeToString(dataHash[:])
|
||||||
|
|
||||||
// setup the server with a local backend in a temporary directory
|
// setup the server with a local backend in a temporary directory
|
||||||
tempdir, err := ioutil.TempDir("", "rest-server-test-")
|
tempdir, err := os.MkdirTemp("", "rest-server-test-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -190,7 +189,7 @@ func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
conf.Path = tempdir
|
conf.Path = tempdir
|
||||||
mux, err := NewHandler(&conf)
|
mux, err := NewHandler(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error from NewHandler: %v", err)
|
t.Fatalf("error from NewHandler: %v", err)
|
||||||
}
|
}
|
||||||
@@ -199,7 +198,7 @@ func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string,
|
|||||||
|
|
||||||
// TestResticAppendOnlyHandler runs tests on the restic handler code, especially in append-only mode.
|
// TestResticAppendOnlyHandler runs tests on the restic handler code, especially in append-only mode.
|
||||||
func TestResticAppendOnlyHandler(t *testing.T) {
|
func TestResticAppendOnlyHandler(t *testing.T) {
|
||||||
mux, data, fileID, _, cleanup := createTestHandler(t, Server{
|
mux, data, fileID, _, cleanup := createTestHandler(t, &Server{
|
||||||
AppendOnly: true,
|
AppendOnly: true,
|
||||||
NoAuth: true,
|
NoAuth: true,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
@@ -300,7 +299,7 @@ func createIdempotentDeleteSeq(t testing.TB, path string, data string) []TestReq
|
|||||||
|
|
||||||
// TestResticHandler runs tests on the restic handler code, especially in append-only mode.
|
// TestResticHandler runs tests on the restic handler code, especially in append-only mode.
|
||||||
func TestResticHandler(t *testing.T) {
|
func TestResticHandler(t *testing.T) {
|
||||||
mux, data, fileID, _, cleanup := createTestHandler(t, Server{
|
mux, data, fileID, _, cleanup := createTestHandler(t, &Server{
|
||||||
NoAuth: true,
|
NoAuth: true,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
PanicOnError: true,
|
PanicOnError: true,
|
||||||
@@ -331,7 +330,7 @@ func TestResticHandler(t *testing.T) {
|
|||||||
|
|
||||||
// TestResticErrorHandler runs tests on the restic handler error handling.
|
// TestResticErrorHandler runs tests on the restic handler error handling.
|
||||||
func TestResticErrorHandler(t *testing.T) {
|
func TestResticErrorHandler(t *testing.T) {
|
||||||
mux, _, _, tempdir, cleanup := createTestHandler(t, Server{
|
mux, _, _, tempdir, cleanup := createTestHandler(t, &Server{
|
||||||
AppendOnly: true,
|
AppendOnly: true,
|
||||||
NoAuth: true,
|
NoAuth: true,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
@@ -380,7 +379,7 @@ func TestResticErrorHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyList(t *testing.T) {
|
func TestEmptyList(t *testing.T) {
|
||||||
mux, _, _, _, cleanup := createTestHandler(t, Server{
|
mux, _, _, _, cleanup := createTestHandler(t, &Server{
|
||||||
AppendOnly: true,
|
AppendOnly: true,
|
||||||
NoAuth: true,
|
NoAuth: true,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
@@ -404,7 +403,7 @@ func TestEmptyList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestListWithUnexpectedFiles(t *testing.T) {
|
func TestListWithUnexpectedFiles(t *testing.T) {
|
||||||
mux, _, _, tempdir, cleanup := createTestHandler(t, Server{
|
mux, _, _, tempdir, cleanup := createTestHandler(t, &Server{
|
||||||
AppendOnly: true,
|
AppendOnly: true,
|
||||||
NoAuth: true,
|
NoAuth: true,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
@@ -510,7 +509,7 @@ func newDelayedErrorReader(err error) *delayErrorReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *delayErrorReader) Read(p []byte) (int, error) {
|
func (d *delayErrorReader) Read(_ []byte) (int, error) {
|
||||||
d.firstReadOnce.Do(func() {
|
d.firstReadOnce.Do(func() {
|
||||||
// close the channel to signal that the first read has happened
|
// close the channel to signal that the first read has happened
|
||||||
close(d.FirstRead)
|
close(d.FirstRead)
|
||||||
@@ -522,7 +521,7 @@ func (d *delayErrorReader) Read(p []byte) (int, error) {
|
|||||||
// TestAbortedRequest runs tests with concurrent upload requests for the same file.
|
// TestAbortedRequest runs tests with concurrent upload requests for the same file.
|
||||||
func TestAbortedRequest(t *testing.T) {
|
func TestAbortedRequest(t *testing.T) {
|
||||||
// the race condition doesn't happen for append-only repositories
|
// the race condition doesn't happen for append-only repositories
|
||||||
mux, _, _, _, cleanup := createTestHandler(t, Server{
|
mux, _, _, _, cleanup := createTestHandler(t, &Server{
|
||||||
NoAuth: true,
|
NoAuth: true,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
PanicOnError: true,
|
PanicOnError: true,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package restserver
|
package restserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -12,7 +11,7 @@ func TestValidate(t *testing.T) {
|
|||||||
rawPwd := "test"
|
rawPwd := "test"
|
||||||
wrongPwd := "wrong"
|
wrongPwd := "wrong"
|
||||||
|
|
||||||
tmpfile, err := ioutil.TempFile("", "rest-validate-")
|
tmpfile, err := os.CreateTemp("", "rest-validate-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
17
mux.go
17
mux.go
@@ -41,10 +41,17 @@ func (s *Server) checkAuth(r *http.Request) (username string, ok bool) {
|
|||||||
if s.NoAuth {
|
if s.NoAuth {
|
||||||
return username, true
|
return username, true
|
||||||
}
|
}
|
||||||
var password string
|
if s.ProxyAuthUsername != "" {
|
||||||
username, password, ok = r.BasicAuth()
|
username = r.Header.Get(s.ProxyAuthUsername)
|
||||||
if !ok || !s.htpasswdFile.Validate(username, password) {
|
if username == "" {
|
||||||
return "", false
|
return "", false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var password string
|
||||||
|
username, password, ok = r.BasicAuth()
|
||||||
|
if !ok || !s.htpasswdFile.Validate(username, password) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return username, true
|
return username, true
|
||||||
}
|
}
|
||||||
@@ -66,7 +73,7 @@ func (s *Server) wrapMetricsAuth(f http.HandlerFunc) http.HandlerFunc {
|
|||||||
|
|
||||||
// NewHandler returns the master HTTP multiplexer/router.
|
// NewHandler returns the master HTTP multiplexer/router.
|
||||||
func NewHandler(server *Server) (http.Handler, error) {
|
func NewHandler(server *Server) (http.Handler, error) {
|
||||||
if !server.NoAuth {
|
if !server.NoAuth && server.ProxyAuthUsername == "" {
|
||||||
var err error
|
var err error
|
||||||
if server.HtpasswdPath == "" {
|
if server.HtpasswdPath == "" {
|
||||||
server.HtpasswdPath = filepath.Join(server.Path, ".htpasswd")
|
server.HtpasswdPath = filepath.Join(server.Path, ".htpasswd")
|
||||||
|
|||||||
80
mux_test.go
Normal file
80
mux_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package restserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckAuth(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
server *Server
|
||||||
|
requestHeaders map[string]string
|
||||||
|
basicAuth bool
|
||||||
|
basicUser string
|
||||||
|
basicPassword string
|
||||||
|
expectedUser string
|
||||||
|
expectedOk bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "NoAuth enabled",
|
||||||
|
server: &Server{
|
||||||
|
NoAuth: true,
|
||||||
|
},
|
||||||
|
expectedOk: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Proxy Auth successful",
|
||||||
|
server: &Server{
|
||||||
|
ProxyAuthUsername: "X-Remote-User",
|
||||||
|
},
|
||||||
|
requestHeaders: map[string]string{
|
||||||
|
"X-Remote-User": "restic",
|
||||||
|
},
|
||||||
|
expectedUser: "restic",
|
||||||
|
expectedOk: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Proxy Auth empty header",
|
||||||
|
server: &Server{
|
||||||
|
ProxyAuthUsername: "X-Remote-User",
|
||||||
|
},
|
||||||
|
requestHeaders: map[string]string{
|
||||||
|
"X-Remote-User": "",
|
||||||
|
},
|
||||||
|
expectedOk: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Proxy Auth missing header",
|
||||||
|
server: &Server{
|
||||||
|
ProxyAuthUsername: "X-Remote-User",
|
||||||
|
},
|
||||||
|
expectedOk: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Proxy Auth send but not enabled",
|
||||||
|
server: &Server{},
|
||||||
|
requestHeaders: map[string]string{
|
||||||
|
"X-Remote-User": "restic",
|
||||||
|
},
|
||||||
|
expectedOk: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest("GET", "/", nil)
|
||||||
|
for header, value := range tt.requestHeaders {
|
||||||
|
req.Header.Set(header, value)
|
||||||
|
}
|
||||||
|
if tt.basicAuth {
|
||||||
|
req.SetBasicAuth(tt.basicUser, tt.basicPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
username, ok := tt.server.checkAuth(req)
|
||||||
|
if username != tt.expectedUser || ok != tt.expectedOk {
|
||||||
|
t.Errorf("expected (%v, %v), got (%v, %v)", tt.expectedUser, tt.expectedOk, username, ok)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,7 +113,7 @@ func tallySize(path string) (int64, error) {
|
|||||||
path = "."
|
path = "."
|
||||||
}
|
}
|
||||||
var size int64
|
var size int64
|
||||||
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
81
repo/repo.go
81
repo/repo.go
@@ -6,7 +6,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -29,8 +28,6 @@ import (
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
AppendOnly bool // if set, delete actions are not allowed
|
AppendOnly bool // if set, delete actions are not allowed
|
||||||
Debug bool
|
Debug bool
|
||||||
DirMode os.FileMode
|
|
||||||
FileMode os.FileMode
|
|
||||||
NoVerifyUpload bool
|
NoVerifyUpload bool
|
||||||
|
|
||||||
// If set, we will panic when an internal server error happens. This
|
// If set, we will panic when an internal server error happens. This
|
||||||
@@ -40,6 +37,13 @@ type Options struct {
|
|||||||
BlobMetricFunc BlobMetricFunc
|
BlobMetricFunc BlobMetricFunc
|
||||||
QuotaManager *quota.Manager
|
QuotaManager *quota.Manager
|
||||||
FsyncWarning *sync.Once
|
FsyncWarning *sync.Once
|
||||||
|
|
||||||
|
// If set makes files group accessible
|
||||||
|
GroupAccessible bool
|
||||||
|
|
||||||
|
// Defaults dir and file mode
|
||||||
|
dirMode os.FileMode
|
||||||
|
fileMode os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultDirMode is the file mode used for directory creation if not
|
// DefaultDirMode is the file mode used for directory creation if not
|
||||||
@@ -50,6 +54,14 @@ const DefaultDirMode os.FileMode = 0700
|
|||||||
// overridden in the Options
|
// overridden in the Options
|
||||||
const DefaultFileMode os.FileMode = 0600
|
const DefaultFileMode os.FileMode = 0600
|
||||||
|
|
||||||
|
// GroupAccessibleDirMode is the file mode used for directory creation when
|
||||||
|
// group access is enabled
|
||||||
|
const GroupAccessibleDirMode os.FileMode = 0770
|
||||||
|
|
||||||
|
// GroupAccessibleFileMode is the file mode used for file creation when
|
||||||
|
// group access is enabled
|
||||||
|
const GroupAccessibleFileMode os.FileMode = 0660
|
||||||
|
|
||||||
// New creates a new Handler for a single Restic backup repo.
|
// New creates a new Handler for a single Restic backup repo.
|
||||||
// path is the full filesystem path to this repo directory.
|
// path is the full filesystem path to this repo directory.
|
||||||
// opt is a set of options.
|
// opt is a set of options.
|
||||||
@@ -57,12 +69,15 @@ func New(path string, opt Options) (*Handler, error) {
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, fmt.Errorf("path is required")
|
return nil, fmt.Errorf("path is required")
|
||||||
}
|
}
|
||||||
if opt.DirMode == 0 {
|
|
||||||
opt.DirMode = DefaultDirMode
|
opt.dirMode = DefaultDirMode
|
||||||
}
|
opt.fileMode = DefaultFileMode
|
||||||
if opt.FileMode == 0 {
|
|
||||||
opt.FileMode = DefaultFileMode
|
if opt.GroupAccessible {
|
||||||
|
opt.dirMode = GroupAccessibleDirMode
|
||||||
|
opt.fileMode = GroupAccessibleFileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
h := Handler{
|
h := Handler{
|
||||||
path: path,
|
path: path,
|
||||||
opt: opt,
|
opt: opt,
|
||||||
@@ -251,7 +266,7 @@ func (h *Handler) wrapFileWriter(r *http.Request, w io.Writer) (io.Writer, int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkConfig checks whether a configuration exists.
|
// checkConfig checks whether a configuration exists.
|
||||||
func (h *Handler) checkConfig(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) checkConfig(w http.ResponseWriter, _ *http.Request) {
|
||||||
if h.opt.Debug {
|
if h.opt.Debug {
|
||||||
log.Println("checkConfig()")
|
log.Println("checkConfig()")
|
||||||
}
|
}
|
||||||
@@ -267,13 +282,13 @@ func (h *Handler) checkConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getConfig allows for a config to be retrieved.
|
// getConfig allows for a config to be retrieved.
|
||||||
func (h *Handler) getConfig(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) getConfig(w http.ResponseWriter, _ *http.Request) {
|
||||||
if h.opt.Debug {
|
if h.opt.Debug {
|
||||||
log.Println("getConfig()")
|
log.Println("getConfig()")
|
||||||
}
|
}
|
||||||
cfg := h.getSubPath("config")
|
cfg := h.getSubPath("config")
|
||||||
|
|
||||||
bytes, err := ioutil.ReadFile(cfg)
|
bytes, err := os.ReadFile(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.fileAccessError(w, err)
|
h.fileAccessError(w, err)
|
||||||
return
|
return
|
||||||
@@ -289,7 +304,7 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
cfg := h.getSubPath("config")
|
cfg := h.getSubPath("config")
|
||||||
|
|
||||||
f, err := os.OpenFile(cfg, os.O_CREATE|os.O_WRONLY|os.O_EXCL, h.opt.FileMode)
|
f, err := os.OpenFile(cfg, os.O_CREATE|os.O_WRONLY|os.O_EXCL, h.opt.fileMode)
|
||||||
if err != nil && os.IsExist(err) {
|
if err != nil && os.IsExist(err) {
|
||||||
if h.opt.Debug {
|
if h.opt.Debug {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
@@ -314,7 +329,7 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deleteConfig removes a config.
|
// deleteConfig removes a config.
|
||||||
func (h *Handler) deleteConfig(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) deleteConfig(w http.ResponseWriter, _ *http.Request) {
|
||||||
if h.opt.Debug {
|
if h.opt.Debug {
|
||||||
log.Println("deleteConfig()")
|
log.Println("deleteConfig()")
|
||||||
}
|
}
|
||||||
@@ -369,7 +384,7 @@ func (h *Handler) listBlobsV1(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
path := h.getSubPath(objectType)
|
path := h.getSubPath(objectType)
|
||||||
|
|
||||||
items, err := ioutil.ReadDir(path)
|
items, err := os.ReadDir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.fileAccessError(w, err)
|
h.fileAccessError(w, err)
|
||||||
return
|
return
|
||||||
@@ -383,8 +398,8 @@ func (h *Handler) listBlobsV1(w http.ResponseWriter, r *http.Request) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
subpath := filepath.Join(path, i.Name())
|
subpath := filepath.Join(path, i.Name())
|
||||||
var subitems []os.FileInfo
|
var subitems []os.DirEntry
|
||||||
subitems, err = ioutil.ReadDir(subpath)
|
subitems, err = os.ReadDir(subpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.fileAccessError(w, err)
|
h.fileAccessError(w, err)
|
||||||
return
|
return
|
||||||
@@ -428,7 +443,7 @@ func (h *Handler) listBlobsV2(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
path := h.getSubPath(objectType)
|
path := h.getSubPath(objectType)
|
||||||
|
|
||||||
items, err := ioutil.ReadDir(path)
|
items, err := os.ReadDir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.fileAccessError(w, err)
|
h.fileAccessError(w, err)
|
||||||
return
|
return
|
||||||
@@ -442,17 +457,27 @@ func (h *Handler) listBlobsV2(w http.ResponseWriter, r *http.Request) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
subpath := filepath.Join(path, i.Name())
|
subpath := filepath.Join(path, i.Name())
|
||||||
var subitems []os.FileInfo
|
var subitems []os.DirEntry
|
||||||
subitems, err = ioutil.ReadDir(subpath)
|
subitems, err = os.ReadDir(subpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.fileAccessError(w, err)
|
h.fileAccessError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, f := range subitems {
|
for _, f := range subitems {
|
||||||
blobs = append(blobs, Blob{Name: f.Name(), Size: f.Size()})
|
fi, err := f.Info()
|
||||||
|
if err != nil {
|
||||||
|
h.fileAccessError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
blobs = append(blobs, Blob{Name: f.Name(), Size: fi.Size()})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
blobs = append(blobs, Blob{Name: i.Name(), Size: i.Size()})
|
fi, err := i.Info()
|
||||||
|
if err != nil {
|
||||||
|
h.fileAccessError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
blobs = append(blobs, Blob{Name: i.Name(), Size: fi.Size()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,15 +570,15 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmpFn := filepath.Join(filepath.Dir(path), objectID+".rest-server-temp")
|
tmpFn := filepath.Join(filepath.Dir(path), objectID+".rest-server-temp")
|
||||||
tf, err := tempFile(tmpFn, h.opt.FileMode)
|
tf, err := tempFile(tmpFn, h.opt.fileMode)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// the error is caused by a missing directory, create it and retry
|
// the error is caused by a missing directory, create it and retry
|
||||||
mkdirErr := os.MkdirAll(filepath.Dir(path), h.opt.DirMode)
|
mkdirErr := os.MkdirAll(filepath.Dir(path), h.opt.dirMode)
|
||||||
if mkdirErr != nil {
|
if mkdirErr != nil {
|
||||||
log.Print(mkdirErr)
|
log.Print(mkdirErr)
|
||||||
} else {
|
} else {
|
||||||
// try again
|
// try again
|
||||||
tf, err = tempFile(tmpFn, h.opt.FileMode)
|
tf, err = tempFile(tmpFn, h.opt.fileMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -652,7 +677,7 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.sendMetric(objectType, BlobWrite, uint64(written))
|
h.sendMetric(objectType, BlobWrite, uint64(written))
|
||||||
}
|
}
|
||||||
|
|
||||||
// tempFile implements a custom version of ioutil.TempFile which allows modifying the file permissions
|
// tempFile implements a custom version of os.CreateTemp which allows modifying the file permissions
|
||||||
func tempFile(fn string, perm os.FileMode) (f *os.File, err error) {
|
func tempFile(fn string, perm os.FileMode) (f *os.File, err error) {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
name := fn + strconv.FormatInt(rand.Int63(), 10)
|
name := fn + strconv.FormatInt(rand.Int63(), 10)
|
||||||
@@ -750,13 +775,13 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
log.Printf("Creating repository directories in %s\n", h.path)
|
log.Printf("Creating repository directories in %s\n", h.path)
|
||||||
|
|
||||||
if err := os.MkdirAll(h.path, h.opt.DirMode); err != nil {
|
if err := os.MkdirAll(h.path, h.opt.dirMode); err != nil {
|
||||||
h.internalServerError(w, err)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range ObjectTypes {
|
for _, d := range ObjectTypes {
|
||||||
if err := os.Mkdir(filepath.Join(h.path, d), h.opt.DirMode); err != nil && !os.IsExist(err) {
|
if err := os.Mkdir(filepath.Join(h.path, d), h.opt.dirMode); err != nil && !os.IsExist(err) {
|
||||||
h.internalServerError(w, err)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -764,7 +789,7 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i))
|
dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i))
|
||||||
if err := os.Mkdir(dirPath, h.opt.DirMode); err != nil && !os.IsExist(err) {
|
if err := os.Mkdir(dirPath, h.opt.dirMode); err != nil && !os.IsExist(err) {
|
||||||
h.internalServerError(w, err)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user