mirror of
https://github.com/restic/restic.git
synced 2025-12-17 17:02:45 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d0aa7f2e3 | ||
|
|
18f18b7f99 | ||
|
|
426b71e3e5 | ||
|
|
b7bb697cf7 | ||
|
|
b12a638322 | ||
|
|
4e0135e628 | ||
|
|
9ef8e13102 | ||
|
|
4940e330c0 | ||
|
|
3a63430b07 | ||
|
|
a5e814bd8d | ||
|
|
398862c5c8 | ||
|
|
b47c67fd90 | ||
|
|
6d7e37edce | ||
|
|
4998fd68a7 | ||
|
|
06cc6017b8 | ||
|
|
37851827c5 | ||
|
|
b75f80ae5f | ||
|
|
31f87b6188 | ||
|
|
b67b88a0c0 | ||
|
|
d57b01d6eb | ||
|
|
fc81df3f54 | ||
|
|
73995b818a | ||
|
|
49abea6952 | ||
|
|
f18b8ad425 | ||
|
|
0a6296bfde | ||
|
|
2403d1f139 | ||
|
|
86a453200a | ||
|
|
518fbbcdc2 | ||
|
|
c62f523e6d | ||
|
|
91e9f65991 | ||
|
|
d839850ed4 | ||
|
|
ac051c3dcd | ||
|
|
20f472a67f | ||
|
|
7b986795de | ||
|
|
4f03e03b2c | ||
|
|
242b607bf6 | ||
|
|
22bbbf42f5 | ||
|
|
3c8fc9d9bc | ||
|
|
5070e62b18 | ||
|
|
d64bad1a90 | ||
|
|
6bdca9a7d5 | ||
|
|
91d582a667 | ||
|
|
ef1e137e7a | ||
|
|
81ac49f59d | ||
|
|
ba2b0b2cc7 | ||
|
|
37a4235e4d | ||
|
|
04898e41d1 | ||
|
|
07e4a78e46 | ||
|
|
236f81758e | ||
|
|
16850c61fa | ||
|
|
67a572fa0d | ||
|
|
4686a12a2d | ||
|
|
4dbed5f905 | ||
|
|
d708c5ea73 | ||
|
|
ee0cb7d1aa | ||
|
|
590dc82719 | ||
|
|
72d70d94f9 | ||
|
|
aaa48e765a | ||
|
|
f61cf4a1e5 | ||
|
|
a22b9d5735 | ||
|
|
e9ae67c968 | ||
|
|
1fe6fbc4b8 | ||
|
|
3d4fb876f4 | ||
|
|
5d182ed1ab |
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -26,10 +26,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
8
.github/workflows/tests.yml
vendored
8
.github/workflows/tests.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go ${{ matrix.go }}
|
||||
uses: actions/setup-go@v5
|
||||
@@ -220,7 +220,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go ${{ env.latest_go }}
|
||||
uses: actions/setup-go@v5
|
||||
@@ -242,7 +242,7 @@ jobs:
|
||||
checks: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go ${{ env.latest_go }}
|
||||
uses: actions/setup-go@v5
|
||||
@@ -287,7 +287,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
|
||||
111
CHANGELOG.md
111
CHANGELOG.md
@@ -1,5 +1,6 @@
|
||||
# Table of Contents
|
||||
|
||||
* [Changelog for 0.18.1](#changelog-for-restic-0181-2025-09-21)
|
||||
* [Changelog for 0.18.0](#changelog-for-restic-0180-2025-03-27)
|
||||
* [Changelog for 0.17.3](#changelog-for-restic-0173-2024-11-08)
|
||||
* [Changelog for 0.17.2](#changelog-for-restic-0172-2024-10-27)
|
||||
@@ -39,6 +40,106 @@
|
||||
* [Changelog for 0.6.0](#changelog-for-restic-060-2017-05-29)
|
||||
|
||||
|
||||
# Changelog for restic 0.18.1 (2025-09-21)
|
||||
The following sections list the changes in restic 0.18.1 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
## Summary
|
||||
|
||||
* Fix #5324: Correctly handle `backup --stdin-filename` with directory paths
|
||||
* Fix #5325: Accept `RESTIC_HOST` environment variable in `forget` command
|
||||
* Fix #5342: Ignore "chmod not supported" errors when writing files
|
||||
* Fix #5344: Ignore `EOPNOTSUPP` errors for extended attributes
|
||||
* Fix #5421: Fix rare crash if directory is removed during backup
|
||||
* Fix #5429: Stop retrying uploads when rest-server runs out of space
|
||||
* Fix #5467: Improve handling of download retries in `check` command
|
||||
|
||||
## Details
|
||||
|
||||
* Bugfix #5324: Correctly handle `backup --stdin-filename` with directory paths
|
||||
|
||||
In restic 0.18.0, the `backup` command failed if a filename that includes at
|
||||
least a directory was passed to `--stdin-filename`. For example,
|
||||
`--stdin-filename /foo/bar` resulted in the following error:
|
||||
|
||||
```
|
||||
Fatal: unable to save snapshot: open /foo: no such file or directory
|
||||
```
|
||||
|
||||
This has now been fixed.
|
||||
|
||||
https://github.com/restic/restic/issues/5324
|
||||
https://github.com/restic/restic/pull/5356
|
||||
|
||||
* Bugfix #5325: Accept `RESTIC_HOST` environment variable in `forget` command
|
||||
|
||||
The `forget` command did not use the host name from the `RESTIC_HOST`
|
||||
environment variable when filtering snapshots. This has now been fixed.
|
||||
|
||||
https://github.com/restic/restic/issues/5325
|
||||
https://github.com/restic/restic/pull/5327
|
||||
|
||||
* Bugfix #5342: Ignore "chmod not supported" errors when writing files
|
||||
|
||||
Restic 0.18.0 introduced a bug that caused `chmod xxx: operation not supported`
|
||||
errors to appear when writing to a local file repository that did not support
|
||||
chmod (like CIFS or WebDAV mounted via FUSE). Restic now ignores those errors.
|
||||
|
||||
https://github.com/restic/restic/issues/5342
|
||||
|
||||
* Bugfix #5344: Ignore `EOPNOTSUPP` errors for extended attributes
|
||||
|
||||
Restic 0.18.0 added extended attribute support for NetBSD 10+, but not all
|
||||
NetBSD filesystems support extended attributes. Other BSD systems can likewise
|
||||
return `EOPNOTSUPP`, so restic now ignores these errors.
|
||||
|
||||
https://github.com/restic/restic/issues/5344
|
||||
|
||||
* Bugfix #5421: Fix rare crash if directory is removed during backup
|
||||
|
||||
In restic 0.18.0, the `backup` command could crash if a directory was removed
|
||||
between reading its metadata and listing its directory content. This has now
|
||||
been fixed.
|
||||
|
||||
https://github.com/restic/restic/pull/5421
|
||||
|
||||
* Bugfix #5429: Stop retrying uploads when rest-server runs out of space
|
||||
|
||||
When rest-server returns a `507 Insufficient Storage` error, it indicates that
|
||||
no more storage capacity is available. Restic now correctly stops retrying
|
||||
uploads in this case.
|
||||
|
||||
https://github.com/restic/restic/issues/5429
|
||||
https://github.com/restic/restic/pull/5452
|
||||
|
||||
* Bugfix #5467: Improve handling of download retries in `check` command
|
||||
|
||||
In very rare cases, the `check` command could unnecessarily report repository
|
||||
damage if the backend returned incomplete, corrupted data on the first download
|
||||
try which is afterwards resolved by a download retry.
|
||||
|
||||
This could result in an error output like the following:
|
||||
|
||||
```
|
||||
Load(<data/34567890ab>, 33918928, 0) returned error, retrying after 871.35598ms: readFull: unexpected EOF
|
||||
Load(<data/34567890ab>, 33918928, 0) operation successful after 1 retries
|
||||
check successful on second attempt, original error pack 34567890ab[...] contains 6 errors: [blob 12345678[...]: decrypting blob <data/12345678> from 34567890 failed: ciphertext verification failed ...]
|
||||
[...]
|
||||
Fatal: repository contains errors
|
||||
```
|
||||
|
||||
This fix only applies to a very specific case where the log shows `operation
|
||||
successful after 1 retries` followed by a `check successful on second attempt,
|
||||
original error` that only reports `ciphertext verification failed` errors in the
|
||||
pack file. If any other errors are reported in the pack file, then the
|
||||
repository still has to be considered as damaged.
|
||||
|
||||
Now, only the check result of the last download retry is reported as intended.
|
||||
|
||||
https://github.com/restic/restic/issues/5467
|
||||
https://github.com/restic/restic/pull/5495
|
||||
|
||||
|
||||
# Changelog for restic 0.18.0 (2025-03-27)
|
||||
The following sections list the changes in restic 0.18.0 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
@@ -54,7 +155,7 @@ restic users. The changes are ordered by importance.
|
||||
* Fix #5249: Fix creation of oversized index by `repair index --read-all-packs`
|
||||
* Fix #5259: Fix rare crash in command output
|
||||
* Chg #4938: Update dependencies and require Go 1.23 or newer
|
||||
* Chg #5162: Promote feature flags
|
||||
* Chg #5162: Graduate feature flags
|
||||
* Enh #1378: Add JSON support to `check` command
|
||||
* Enh #2511: Support generating shell completions to stdout
|
||||
* Enh #3697: Allow excluding online-only cloud files (e.g. OneDrive)
|
||||
@@ -76,7 +177,7 @@ restic users. The changes are ordered by importance.
|
||||
* Enh #5173: Add experimental S3 cold storage support
|
||||
* Enh #5174: Add xattr support for NetBSD 10+
|
||||
* Enh #5251: Improve retry handling for flaky `rclone` backends
|
||||
* Enh #52897: Make `recover` automatically rebuild index when needed
|
||||
* Enh #5287: Make `recover` automatically rebuild index when needed
|
||||
|
||||
## Details
|
||||
|
||||
@@ -208,7 +309,7 @@ restic users. The changes are ordered by importance.
|
||||
|
||||
https://github.com/restic/restic/pull/4938
|
||||
|
||||
* Change #5162: Promote feature flags
|
||||
* Change #5162: Graduate feature flags
|
||||
|
||||
The `deprecate-legacy-index`, `deprecate-s3-legacy-layout`,
|
||||
`explicit-s3-anonymous-auth` and `safe-forget-keep-tags` features are now stable
|
||||
@@ -408,13 +509,13 @@ restic users. The changes are ordered by importance.
|
||||
|
||||
https://github.com/restic/restic/pull/5251
|
||||
|
||||
* Enhancement #52897: Make `recover` automatically rebuild index when needed
|
||||
* Enhancement #5287: Make `recover` automatically rebuild index when needed
|
||||
|
||||
When trying to recover data from an interrupted snapshot, it was previously
|
||||
necessary to manually run `repair index` before runnning `recover`. This now
|
||||
happens automatically so that only `recover` is necessary.
|
||||
|
||||
https://github.com/restic/restic/issues/52897
|
||||
https://github.com/restic/restic/issues/5287
|
||||
https://github.com/restic/restic/pull/5296
|
||||
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ When trying to recover data from an interrupted snapshot, it was previously
|
||||
necessary to manually run `repair index` before runnning `recover`. This now
|
||||
happens automatically so that only `recover` is necessary.
|
||||
|
||||
https://github.com/restic/restic/issues/52897
|
||||
https://github.com/restic/restic/issues/5287
|
||||
https://github.com/restic/restic/pull/5296
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Change: Promote feature flags
|
||||
Change: Graduate feature flags
|
||||
|
||||
The `deprecate-legacy-index`, `deprecate-s3-legacy-layout`,
|
||||
`explicit-s3-anonymous-auth` and `safe-forget-keep-tags` features are
|
||||
|
||||
14
changelog/0.18.1_2025-09-21/issue-5324
Normal file
14
changelog/0.18.1_2025-09-21/issue-5324
Normal file
@@ -0,0 +1,14 @@
|
||||
Bugfix: Correctly handle `backup --stdin-filename` with directory paths
|
||||
|
||||
In restic 0.18.0, the `backup` command failed if a filename that includes
|
||||
at least a directory was passed to `--stdin-filename`. For example,
|
||||
`--stdin-filename /foo/bar` resulted in the following error:
|
||||
|
||||
```
|
||||
Fatal: unable to save snapshot: open /foo: no such file or directory
|
||||
```
|
||||
|
||||
This has now been fixed.
|
||||
|
||||
https://github.com/restic/restic/issues/5324
|
||||
https://github.com/restic/restic/pull/5356
|
||||
7
changelog/0.18.1_2025-09-21/issue-5325
Normal file
7
changelog/0.18.1_2025-09-21/issue-5325
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Accept `RESTIC_HOST` environment variable in `forget` command
|
||||
|
||||
The `forget` command did not use the host name from the `RESTIC_HOST`
|
||||
environment variable when filtering snapshots. This has now been fixed.
|
||||
|
||||
https://github.com/restic/restic/issues/5325
|
||||
https://github.com/restic/restic/pull/5327
|
||||
7
changelog/0.18.1_2025-09-21/issue-5342
Normal file
7
changelog/0.18.1_2025-09-21/issue-5342
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Ignore "chmod not supported" errors when writing files
|
||||
|
||||
Restic 0.18.0 introduced a bug that caused `chmod xxx: operation not supported`
|
||||
errors to appear when writing to a local file repository that did not support
|
||||
chmod (like CIFS or WebDAV mounted via FUSE). Restic now ignores those errors.
|
||||
|
||||
https://github.com/restic/restic/issues/5342
|
||||
7
changelog/0.18.1_2025-09-21/issue-5344
Normal file
7
changelog/0.18.1_2025-09-21/issue-5344
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Ignore `EOPNOTSUPP` errors for extended attributes
|
||||
|
||||
Restic 0.18.0 added extended attribute support for NetBSD 10+, but not all
|
||||
NetBSD filesystems support extended attributes. Other BSD systems can
|
||||
likewise return `EOPNOTSUPP`, so restic now ignores these errors.
|
||||
|
||||
https://github.com/restic/restic/issues/5344
|
||||
8
changelog/0.18.1_2025-09-21/issue-5429
Normal file
8
changelog/0.18.1_2025-09-21/issue-5429
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Stop retrying uploads when rest-server runs out of space
|
||||
|
||||
When rest-server returns a `507 Insufficient Storage` error, it indicates
|
||||
that no more storage capacity is available. Restic now correctly stops
|
||||
retrying uploads in this case.
|
||||
|
||||
https://github.com/restic/restic/issues/5429
|
||||
https://github.com/restic/restic/pull/5452
|
||||
27
changelog/0.18.1_2025-09-21/issue-5467
Normal file
27
changelog/0.18.1_2025-09-21/issue-5467
Normal file
@@ -0,0 +1,27 @@
|
||||
Bugfix: Improve handling of download retries in `check` command
|
||||
|
||||
In very rare cases, the `check` command could unnecessarily report repository
|
||||
damage if the backend returned incomplete, corrupted data on the first download
|
||||
try which is afterwards resolved by a download retry.
|
||||
|
||||
This could result in an error output like the following:
|
||||
|
||||
```
|
||||
Load(<data/34567890ab>, 33918928, 0) returned error, retrying after 871.35598ms: readFull: unexpected EOF
|
||||
Load(<data/34567890ab>, 33918928, 0) operation successful after 1 retries
|
||||
check successful on second attempt, original error pack 34567890ab[...] contains 6 errors: [blob 12345678[...]: decrypting blob <data/12345678> from 34567890 failed: ciphertext verification failed ...]
|
||||
[...]
|
||||
Fatal: repository contains errors
|
||||
```
|
||||
|
||||
This fix only applies to a very specific case where the log shows
|
||||
`operation successful after 1 retries` followed by a
|
||||
`check successful on second attempt, original error` that only reports
|
||||
`ciphertext verification failed` errors in the pack file. If any other errors
|
||||
are reported in the pack file, then the repository still has to be considered
|
||||
as damaged.
|
||||
|
||||
Now, only the check result of the last download retry is reported as intended.
|
||||
|
||||
https://github.com/restic/restic/issues/5467
|
||||
https://github.com/restic/restic/pull/5495
|
||||
7
changelog/0.18.1_2025-09-21/pull-5421
Normal file
7
changelog/0.18.1_2025-09-21/pull-5421
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Fix rare crash if directory is removed during backup
|
||||
|
||||
In restic 0.18.0, the `backup` command could crash if a directory was removed
|
||||
between reading its metadata and listing its directory content. This has now
|
||||
been fixed.
|
||||
|
||||
https://github.com/restic/restic/pull/5421
|
||||
@@ -591,11 +591,12 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
|
||||
return err
|
||||
}
|
||||
}
|
||||
targetFS = &fs.Reader{
|
||||
ModTime: timeStamp,
|
||||
Name: filename,
|
||||
Mode: 0644,
|
||||
ReadCloser: source,
|
||||
targetFS, err = fs.NewReader(filename, source, fs.ReaderOptions{
|
||||
ModTime: timeStamp,
|
||||
Mode: 0644,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to backup from stdin: %w", err)
|
||||
}
|
||||
targets = []string{filename}
|
||||
}
|
||||
|
||||
@@ -632,12 +632,15 @@ func TestStdinFromCommand(t *testing.T) {
|
||||
|
||||
testSetupBackupData(t, env)
|
||||
opts := BackupOptions{
|
||||
StdinCommand: true,
|
||||
StdinFilename: "stdin",
|
||||
StdinCommand: true,
|
||||
// test that subdirectories are handled correctly
|
||||
StdinFilename: "stdin/subdir/file",
|
||||
}
|
||||
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"python", "-c", "import sys; print('something'); sys.exit(0)"}, opts, env.gopts)
|
||||
testListSnapshots(t, env.gopts, 1)
|
||||
snapshots := testListSnapshots(t, env.gopts, 1)
|
||||
files := testRunLs(t, env.gopts, snapshots[0].String())
|
||||
rtest.Assert(t, includes(files, "/stdin/subdir/file"), "file %q missing from snapshot, got %v", "stdin/subdir/file", files)
|
||||
|
||||
testRunCheck(t, env.gopts)
|
||||
}
|
||||
|
||||
@@ -137,13 +137,14 @@ func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) {
|
||||
f.Var(&opts.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
|
||||
f.BoolVar(&opts.UnsafeAllowRemoveAll, "unsafe-allow-remove-all", false, "allow deleting all snapshots of a snapshot group")
|
||||
|
||||
initMultiSnapshotFilter(f, &opts.SnapshotFilter, false)
|
||||
f.StringArrayVar(&opts.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)")
|
||||
err := f.MarkDeprecated("hostname", "use --host")
|
||||
if err != nil {
|
||||
// MarkDeprecated only returns an error when the flag is not found
|
||||
panic(err)
|
||||
}
|
||||
// must be defined after `--hostname` to not override the default value from the environment
|
||||
initMultiSnapshotFilter(f, &opts.SnapshotFilter, false)
|
||||
|
||||
f.BoolVarP(&opts.Compact, "compact", "c", false, "use compact output format")
|
||||
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestForgetPolicyValues(t *testing.T) {
|
||||
@@ -92,3 +93,10 @@ func TestForgetOptionValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgetHostnameDefaulting(t *testing.T) {
|
||||
t.Setenv("RESTIC_HOST", "testhost")
|
||||
opts := ForgetOptions{}
|
||||
opts.AddFlags(pflag.NewFlagSet("test", pflag.ContinueOnError))
|
||||
rtest.Equals(t, []string{"testhost"}, opts.Hosts)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ import (
|
||||
// to a missing backend storage location or config file
|
||||
var ErrNoRepository = errors.New("repository does not exist")
|
||||
|
||||
var version = "0.18.0"
|
||||
var version = "0.18.1"
|
||||
|
||||
// TimeFormat is the format used for all timestamps printed by restic.
|
||||
const TimeFormat = "2006-01-02 15:04:05"
|
||||
|
||||
@@ -30,14 +30,12 @@ func registerProfiling(cmd *cobra.Command) {
|
||||
return profiler.Start(profiler.opts)
|
||||
}
|
||||
|
||||
origPostRun := cmd.PersistentPostRunE
|
||||
cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// Once https://github.com/spf13/cobra/issues/1893 is fixed,
|
||||
// this could use PersistentPostRunE instead of OnFinalize,
|
||||
// reverting https://github.com/restic/restic/pull/5373.
|
||||
cobra.OnFinalize(func() {
|
||||
profiler.Stop()
|
||||
if origPostRun != nil {
|
||||
return origPostRun(cmd, args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
|
||||
profiler.opts.AddFlags(cmd.PersistentFlags())
|
||||
}
|
||||
|
||||
@@ -74,6 +74,16 @@ avoid any conflicts:
|
||||
|
||||
$ dnf copr remove copart/restic
|
||||
|
||||
Gentoo Linux
|
||||
============
|
||||
|
||||
On `Gentoo Linux <https://www.gentoo.org/>`__, you can install the ``restic``
|
||||
package from the official repos using ``emerge``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# emerge restic
|
||||
|
||||
macOS
|
||||
=====
|
||||
|
||||
@@ -175,15 +185,19 @@ restic can be installed from the official repo of Solus via the ``eopkg`` packag
|
||||
Windows
|
||||
=======
|
||||
|
||||
restic can be installed using `Scoop <https://scoop.sh/>`__:
|
||||
restic can be installed using either `Scoop <https://scoop.sh/>`__ or `WinGet <https://learn.microsoft.com/en-us/windows/package-manager/>`__.
|
||||
|
||||
Regardless of the method, the ``restic.exe`` binary will be added to your ``PATH`` automatically, making the ``restic`` command accessible in Powershell or CMD.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
scoop install restic
|
||||
|
||||
Using this installation method, ``restic.exe`` will automatically be available
|
||||
in the ``PATH``. It can be called from cmd.exe or PowerShell by typing ``restic``.
|
||||
.. code-block:: console
|
||||
|
||||
winget install --exact --id restic.restic --scope Machine
|
||||
|
||||
By default, WinGet will install restic into the ``User`` scope, which is typically in your user's ``%LOCALAPPDATA%`` directory. This behavior may be undesirable for system-wide backups, so specifying ``--scope Machine`` is recommended so that restic is installed into ``%ProgramFiles%``. This requires elevation.
|
||||
|
||||
.. _official_binaries:
|
||||
|
||||
@@ -253,13 +267,6 @@ the `restic beta download site
|
||||
and ready to run, and a new version is built every time a push is made to the
|
||||
master branch.
|
||||
|
||||
Windows
|
||||
=======
|
||||
|
||||
On Windows, put the `restic.exe` binary into `%SystemRoot%\\System32` to use restic
|
||||
in scripts without the need for absolute paths to the binary. This requires
|
||||
administrator rights.
|
||||
|
||||
Docker Container
|
||||
****************
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ Wasabi
|
||||
|
||||
S3 storage from `Wasabi <https://wasabi.com>`__ can be used as follows.
|
||||
|
||||
- Determine the correct Wasabi service URL for your bucket `here <https://wasabi-support.zendesk.com/hc/en-us/articles/360015106031-What-are-the-service-URLs-for-Wasabi-s-different-regions->`__.
|
||||
- Determine the correct Wasabi service URL for your bucket `here <https://docs.wasabi.com/v1/docs/what-are-the-service-urls-for-wasabi-s-different-storage-regions>`__.
|
||||
- Set environment variables with the necessary account credentials
|
||||
|
||||
.. code-block:: console
|
||||
@@ -854,6 +854,6 @@ with an empty password, use the following command.
|
||||
restic init --insecure-no-password
|
||||
|
||||
|
||||
The ``init`` and ``copy`` command also support the option ``--from-insecure-no-password``
|
||||
The ``init`` and ``copy`` commands also support the option ``--from-insecure-no-password``
|
||||
which applies to the source repository. The ``key add`` and ``key passwd`` commands
|
||||
include the ``--new-insecure-no-password`` option to add or set and empty password.
|
||||
include the ``--new-insecure-no-password`` option to add or set an empty password.
|
||||
|
||||
@@ -346,9 +346,10 @@ A trailing ``/`` is ignored, a leading ``/`` anchors the pattern at the root dir
|
||||
This means, ``/bin`` matches ``/bin/bash`` but does not match ``/usr/bin/restic``.
|
||||
|
||||
Regular wildcards cannot be used to match over the directory separator ``/``,
|
||||
e.g. ``b*ash`` matches ``/bin/bash`` but does not match ``/bin/ash``. For this,
|
||||
the special wildcard ``**`` can be used to match arbitrary sub-directories: The
|
||||
pattern ``foo/**/bar`` matches:
|
||||
e.g. ``b*ash`` matches ``/bin/bash`` but does not match ``/bin/ash``. To match
|
||||
across an arbitrary number of subdirectories, use the special ``**`` wildcard.
|
||||
The ``**`` must be positioned between path separators. The pattern
|
||||
``foo/**/bar`` matches:
|
||||
|
||||
* ``/dir1/foo/dir2/bar/file``
|
||||
* ``/foo/bar/file``
|
||||
|
||||
@@ -297,7 +297,7 @@ Note that it is not possible to change the chunker parameters of an existing rep
|
||||
Removing files from snapshots
|
||||
=============================
|
||||
|
||||
Snapshots sometimes turn out to include more files that intended. Instead of
|
||||
Snapshots sometimes turn out to include more files than intended. Instead of
|
||||
removing the snapshots entirely and running the corresponding backup commands
|
||||
again (which is not always practical after the fact) it is possible to remove
|
||||
the unwanted files from affected snapshots by rewriting them using the
|
||||
@@ -338,8 +338,8 @@ the only fields added are ``TotalFilesProcessed`` and ``TotalBytesProcessed``.
|
||||
|
||||
By default, the ``rewrite`` command will keep the original snapshots and create
|
||||
new ones for every snapshot which was modified during rewriting. The new
|
||||
snapshots are marked with the tag ``rewrite`` to differentiate them from the
|
||||
original, rewritten snapshots.
|
||||
snapshots are marked with the tag ``rewrite`` to distinguish them from the
|
||||
original, untouched snapshots.
|
||||
|
||||
Alternatively, you can use the ``--forget`` option to immediately remove the
|
||||
original snapshots. In this case, no tag is added to the new snapshots. Please
|
||||
|
||||
@@ -34,11 +34,12 @@ Backend Connections
|
||||
|
||||
Restic uses a global limit for the number of concurrent connections to a backend.
|
||||
This limit can be configured using ``-o <backend-name>.connections=5``, for example for
|
||||
the REST backend the parameter would be ``-o rest.connections=5``. By default restic uses
|
||||
``5`` connections for each backend, except for the local backend which uses a limit of ``2``.
|
||||
The defaults should work well in most cases. For high-latency backends it can be beneficial
|
||||
to increase the number of connections. Please be aware that this increases the resource
|
||||
consumption of restic and that a too high connection count *will degrade performance*.
|
||||
the REST backend the parameter would be ``-o rest.connections=5`` or for the local backend
|
||||
``-o local.connections=2``. By default restic uses ``5`` connections for each backend,
|
||||
except for the local backend which uses a limit of ``2``. The defaults should work well in
|
||||
most cases. For high-latency backends it can be beneficial to increase the number of
|
||||
connections. Please be aware that this increases the resource consumption of restic and
|
||||
that a too high connection count *will degrade performance*.
|
||||
|
||||
|
||||
CPU Usage
|
||||
|
||||
@@ -71,8 +71,17 @@ The program can be built with debug support like this:
|
||||
$ go run build.go -tags debug
|
||||
|
||||
This will make the ``restic debug <subcommand>`` available which can be used to
|
||||
inspect internal data structures. In addition, this enables profiling support
|
||||
which can help with investigation performance and memory usage issues.
|
||||
inspect internal data structures.
|
||||
|
||||
In addition, this enables profiling flags such as ``--cpu-profile`` and
|
||||
``--mem-profile`` which can help with investigation performance and memory usage
|
||||
issues. See ``restic help`` for more details and a few additional
|
||||
``--...-profile`` flags.
|
||||
|
||||
Running Restic with profiling enabled generates a ``.pprof`` file such as
|
||||
``cpu.pprof``. To view a profile in a web browser, first make sure that the
|
||||
``dot`` command from `Graphviz <https://graphviz.org/>`__ is in the PATH. Then,
|
||||
run ``go tool pprof -http : cpu.pprof``.
|
||||
|
||||
|
||||
************
|
||||
@@ -121,14 +130,17 @@ Backward compatibility for backups is important so that our users are
|
||||
always able to restore saved data. Therefore restic follows `Semantic
|
||||
Versioning <https://semver.org>`__ to clearly define which versions are
|
||||
compatible. The repository and data structures contained therein are
|
||||
considered the "Public API" in the sense of Semantic Versioning. This
|
||||
goes for all released versions of restic, this may not be the case for
|
||||
the master branch.
|
||||
considered the "Public API" in the sense of Semantic Versioning.
|
||||
|
||||
We guarantee backward compatibility of all repositories within one major
|
||||
version; as long as we do not increment the major version, data can be
|
||||
read and restored. We strive to be fully backward compatible to all
|
||||
prior versions.
|
||||
Once version 1.0.0 is released, we guarantee backward compatibility of
|
||||
all repositories within one major version; as long as we do not
|
||||
increment the major version, data can be read and restored. We strive
|
||||
to be fully backward compatible to all prior versions.
|
||||
|
||||
During initial development (versions prior to 1.0.0), maintainers and
|
||||
developers will do their utmost to keep backwards compatibility and
|
||||
stability, although there might be breaking changes without increasing
|
||||
the major version.
|
||||
|
||||
**********************
|
||||
Building documentation
|
||||
|
||||
@@ -18,7 +18,7 @@ depends on the following things:
|
||||
* The path to the Go workspace (``GOPATH=/home/build/go``)
|
||||
* Other environment variables (mostly ``$GOOS``, ``$GOARCH``, ``$CGO_ENABLED``)
|
||||
|
||||
In addition, The compressed ZIP files for Windows depends on the modification
|
||||
In addition, the compressed ZIP files for Windows depends on the modification
|
||||
timestamp and filename of the binary contained in it. In order to reproduce the
|
||||
exact same ZIP file every time, we update the timestamp of the file ``VERSION``
|
||||
in the source code archive and set the timezone to Europe/Berlin.
|
||||
@@ -52,10 +52,10 @@ In the following example, we'll use the file ``restic-0.14.0.tar.gz`` and Go
|
||||
$ go version
|
||||
go version go1.19 linux/amd64
|
||||
|
||||
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate -o restic_linux_amd64 ./cmd/restic
|
||||
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate,disable_grpc_modules -o restic_linux_amd64 ./cmd/restic
|
||||
$ bzip2 restic_linux_amd64
|
||||
|
||||
$ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate -o restic_0.14.0_windows_amd64.exe ./cmd/restic
|
||||
$ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -tags selfupdate,disable_grpc_modules -o restic_0.14.0_windows_amd64.exe ./cmd/restic
|
||||
$ touch --reference VERSION restic_0.14.0_windows_amd64.exe
|
||||
$ TZ=Europe/Berlin zip -q -X restic_0.14.0_windows_amd64.zip restic_0.14.0_windows_amd64.exe
|
||||
|
||||
@@ -66,7 +66,7 @@ The released binaries for restic are built using a Docker container. You can
|
||||
find it on `Docker Hub <https://hub.docker.com/r/restic/builder>`__ as
|
||||
``restic/builder``, the ``Dockerfile`` and instructions on how to build the
|
||||
container can be found in the `GitHub repository
|
||||
<https://github.com/restic/builder>`__
|
||||
<https://github.com/restic/builder>`__.
|
||||
|
||||
The container serves the following goals:
|
||||
* Have a very controlled environment which is independent from the local system
|
||||
@@ -93,7 +93,7 @@ The following steps are necessary to build the binaries:
|
||||
|
||||
mkdir output
|
||||
|
||||
3. Mount the source code and the output directory in the container and run the default command, which starts ``helpers/build-release-binaries/main.go``:
|
||||
4. Mount the source code and the output directory in the container and run the default command, which starts ``helpers/build-release-binaries/main.go``:
|
||||
|
||||
.. code::
|
||||
|
||||
@@ -103,7 +103,7 @@ The following steps are necessary to build the binaries:
|
||||
restic/builder \
|
||||
go run helpers/build-release-binaries/main.go --version 0.14.0
|
||||
|
||||
4. If anything goes wrong, you can enable debug output like this:
|
||||
5. If anything goes wrong, you can enable debug output like this:
|
||||
|
||||
.. code::
|
||||
|
||||
@@ -114,7 +114,7 @@ The following steps are necessary to build the binaries:
|
||||
go run helpers/build-release-binaries/main.go --version 0.14.0 --verbose
|
||||
|
||||
Verifying SLSA Provenance for GHCR Docker Images
|
||||
*******************************************
|
||||
************************************************
|
||||
|
||||
Our Docker images in the GitHub Container Registry (GHCR) are built with SLSA
|
||||
(Supply-chain Levels for Software Artifacts) provenance.
|
||||
@@ -131,9 +131,8 @@ To verify this provenance:
|
||||
--source-uri github.com/restic/restic \
|
||||
<image-name>@<digest>
|
||||
|
||||
Replace `<tag>` with the Git tag of the release you're verifying, `<image-name>`
|
||||
with the full name of the Docker image (including the registry), and `<digest>`
|
||||
with the SHA256 digest of the image.
|
||||
Replace `<image-name>` with the full name of the Docker image (including the registry),
|
||||
and `<digest>` with the SHA256 digest of the image.
|
||||
|
||||
3. If the verification is successful, you'll see output indicating that the provenance
|
||||
is valid.
|
||||
@@ -146,7 +145,7 @@ Verifying the Official Binaries
|
||||
|
||||
To verify the official binaries, you can either build them yourself using the above
|
||||
instructions or use the ``helpers/verify-release-binaries.sh`` script from the restic
|
||||
repository. Run it as ``helpers/verify-release-binaries.sh restic_version go_version``.
|
||||
repository. Run it as ``helpers/verify-release-binaries.sh $restic_version $go_version``.
|
||||
The specified go compiler version must match the one used to build the official
|
||||
binaries. For example, for restic 0.16.2 the command would be
|
||||
``helpers/verify-release-binaries.sh 0.16.2 1.21.3``.
|
||||
|
||||
32
doc/faq.rst
32
doc/faq.rst
@@ -110,9 +110,8 @@ How can I specify encryption passwords automatically?
|
||||
When you run ``restic backup``, you need to enter the passphrase on
|
||||
the console. This is not very convenient for automated backups, so you
|
||||
can also provide the password through the ``--password-file`` option, or one of
|
||||
the environment variables ``RESTIC_PASSWORD`` or ``RESTIC_PASSWORD_FILE``.
|
||||
A discussion is in progress over implementing unattended backups happens in
|
||||
:issue:`533`.
|
||||
the environment variables: ``RESTIC_PASSWORD``, ``RESTIC_PASSWORD_FILE``,
|
||||
or ``RESTIC_PASSWORD_COMMAND``.
|
||||
|
||||
.. important:: Be careful how you set the environment; using the env
|
||||
command, a `system()` call or using inline shell
|
||||
@@ -124,10 +123,33 @@ A discussion is in progress over implementing unattended backups happens in
|
||||
`accessible only to that user`_. Please make sure that
|
||||
the permissions on the files where the password is
|
||||
eventually stored are safe (e.g. `0600` and owned by
|
||||
root).
|
||||
root). Note also that ``RESTIC_PASSWORD_COMMAND`` is
|
||||
safe because it does not export the password itself to
|
||||
the environment.
|
||||
|
||||
.. _accessible only to that user: https://security.stackexchange.com/questions/14000/environment-variable-accessibility-in-linux/14009#14009
|
||||
|
||||
On platforms with an available keychain, keyring or similar secret store, a
|
||||
user can add and then dynamically retrieve passwords, cloud credentials,
|
||||
repository paths, or any other data deemed sensitive. Here's an example of
|
||||
part of a shell script using the `built-in`_ ``security`` command on macOS
|
||||
to retrieve credentials from the system's Keychain before running various
|
||||
``restic`` commands:
|
||||
|
||||
.. _built-in: https://ss64.com/mac/security.html
|
||||
|
||||
::
|
||||
|
||||
export GOOGLE_PROJECT_ID=$(security find-generic-password -a resticGCS -s restic_project_ID -w)
|
||||
|
||||
export GOOGLE_APPLICATION_CREDENTIALS=$(security find-generic-password -a resticGCS -s restic_key -w)
|
||||
|
||||
export RESTIC_REPOSITORY=$(security find-generic-password -a resticGCS -s restic_repo_path -w)
|
||||
|
||||
export RESTIC_PASSWORD_COMMAND='security find-generic-password -a resticGCS -s restic_pwd -w'
|
||||
|
||||
|
||||
|
||||
How to prioritize restic's IO and CPU time
|
||||
------------------------------------------
|
||||
|
||||
@@ -253,7 +275,7 @@ Archive** storage classes is available:
|
||||
.. code-block:: console
|
||||
|
||||
$ restic backup -o s3.storage-class=GLACIER somedir/
|
||||
$ RESTIC_FEATURES=s3-restore restic restore -o s3.enable-restore=1 -o s3.restore-days=7 -o s3.restore-timeout=1d latest
|
||||
$ RESTIC_FEATURES=s3-restore restic restore -o s3.enable-restore=1 -o s3.restore-days=7 -o s3.restore-timeout=24h latest
|
||||
|
||||
**Notes:**
|
||||
|
||||
|
||||
107
go.mod
107
go.mod
@@ -7,10 +7,10 @@ go 1.23.0
|
||||
godebug winsymlink=0
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.51.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
|
||||
cloud.google.com/go/storage v1.56.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
|
||||
github.com/Backblaze/blazer v0.7.2
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/anacrolix/fuse v0.3.1
|
||||
@@ -21,42 +21,42 @@ require (
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/minio/minio-go/v7 v7.0.88
|
||||
github.com/ncw/swift/v2 v2.0.3
|
||||
github.com/peterbourgon/unixtransport v0.0.4
|
||||
github.com/minio/minio-go/v7 v7.0.95
|
||||
github.com/ncw/swift/v2 v2.0.4
|
||||
github.com/peterbourgon/unixtransport v0.0.6
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/profile v1.7.0
|
||||
github.com/pkg/sftp v1.13.8
|
||||
github.com/pkg/xattr v0.4.10
|
||||
github.com/pkg/sftp v1.13.9
|
||||
github.com/pkg/xattr v0.4.12
|
||||
github.com/restic/chunker v0.4.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/term v0.30.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/time v0.11.0
|
||||
google.golang.org/api v0.227.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/term v0.34.0
|
||||
golang.org/x/text v0.28.0
|
||||
golang.org/x/time v0.12.0
|
||||
google.golang.org/api v0.248.0
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.2 // indirect
|
||||
cloud.google.com/go v0.118.3 // indirect
|
||||
cloud.google.com/go/auth v0.15.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.4.1 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
cloud.google.com/go v0.121.6 // indirect
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
@@ -64,38 +64,43 @@ require (
|
||||
github.com/felixge/fgprof v0.9.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/crc64nvme v1.0.2 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/grpc v1.74.2 // indirect
|
||||
google.golang.org/protobuf v1.36.7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
249
go.sum
249
go.sum
@@ -1,51 +1,51 @@
|
||||
cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
|
||||
cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
|
||||
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
|
||||
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
|
||||
cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
|
||||
cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
|
||||
cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
|
||||
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
|
||||
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
|
||||
cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
|
||||
cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
|
||||
cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
|
||||
cloud.google.com/go/storage v1.51.0 h1:ZVZ11zCiD7b3k+cH5lQs/qcNaoSz3U9I0jgwVzqDlCw=
|
||||
cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqvpi5kRKGgc=
|
||||
cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
|
||||
cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
cloud.google.com/go/storage v1.56.1 h1:n6gy+yLnHn0hTwBFzNn8zJ1kqWfR91wzdM8hjRF4wP0=
|
||||
cloud.google.com/go/storage v1.56.1/go.mod h1:C9xuCZgFl3buo2HZU/1FncgvvOgTAs/rnh4gF4lMg0s=
|
||||
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
|
||||
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 h1:ci6Yd6nysBRLEodoziB6ah1+YOzZbZk+NYneoA6q+6E=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/Backblaze/blazer v0.7.2 h1:UWNHMLB+Nf+UmbO2qkVvgriODLEMz4kIyr2Hm+DVXQM=
|
||||
github.com/Backblaze/blazer v0.7.2/go.mod h1:T4y3EYa9IQ5J0PKc/C/J8/CEnSd3qa/lgNw938wZg10=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
|
||||
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
@@ -65,16 +65,15 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
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/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
@@ -96,17 +95,19 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@@ -124,20 +125,20 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -151,19 +152,21 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
|
||||
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
|
||||
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.88 h1:v8MoIJjwYxOkehp+eiLIuvXk87P2raUtoU5klrAAshs=
|
||||
github.com/minio/minio-go/v7 v7.0.88/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg=
|
||||
github.com/ncw/swift/v2 v2.0.3 h1:8R9dmgFIWs+RiVlisCEfiQiik1hjuR0JnOkLxaP9ihg=
|
||||
github.com/ncw/swift/v2 v2.0.3/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
||||
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
||||
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
||||
github.com/ncw/swift/v2 v2.0.4 h1:hHWVFxn5/YaTWAASmn4qyq2p6OyP/Hm3vMLzkjEqR7w=
|
||||
github.com/ncw/swift/v2 v2.0.4/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/ff/v3 v3.3.1/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
|
||||
github.com/peterbourgon/unixtransport v0.0.4 h1:UTF0FxXCAglvoZz9jaGPYjEg52DjBLDYGMJvJni6Tfw=
|
||||
github.com/peterbourgon/unixtransport v0.0.4/go.mod h1:o8aUkOCa8W/BIXpi15uKvbSabjtBh0JhSOJGSfoOhAU=
|
||||
github.com/peterbourgon/unixtransport v0.0.6 h1:sWIViDoRIg2MeyRbWTUDdvvY5XbKaaSr9ionmzNGXWY=
|
||||
github.com/peterbourgon/unixtransport v0.0.6/go.mod h1:o8aUkOCa8W/BIXpi15uKvbSabjtBh0JhSOJGSfoOhAU=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -171,18 +174,17 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
github.com/pkg/sftp v1.13.8 h1:Xt7eJ/xqXv7s0VuzFw7JXhZj6Oc1zI6l4GK8KP9sFB0=
|
||||
github.com/pkg/sftp v1.13.8/go.mod h1:DmvEkvKE2lshEeuo2JMp06yqcx9HVnR7e3zqQl42F3U=
|
||||
github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA=
|
||||
github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
|
||||
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
|
||||
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
|
||||
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/restic/chunker v0.4.0 h1:YUPYCUn70MYP7VO4yllypp2SjmsRhRJaad3xKu1QFRw=
|
||||
github.com/restic/chunker v0.4.0/go.mod h1:z0cH2BejpW636LXw0R/BGyv+Ey8+m9QGiOanDHItzyw=
|
||||
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
|
||||
@@ -195,10 +197,13 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
||||
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@@ -210,30 +215,34 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
|
||||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -243,8 +252,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
@@ -266,10 +275,10 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -278,8 +287,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -299,8 +308,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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=
|
||||
@@ -311,8 +320,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -324,10 +333,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
@@ -339,18 +348,18 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
|
||||
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
|
||||
google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=
|
||||
google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
|
||||
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -264,6 +264,11 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
|
||||
// nodeFromFileInfo returns the restic node from an os.FileInfo.
|
||||
func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) {
|
||||
node, err := meta.ToNode(ignoreXattrListError)
|
||||
// node does not exist. This prevents all further processing for this file.
|
||||
// If an error and a node are returned, then preserve as much data as possible (see below).
|
||||
if err != nil && node == nil {
|
||||
return nil, err
|
||||
}
|
||||
if !arch.WithAtime {
|
||||
node.AccessTime = node.ModTime
|
||||
}
|
||||
@@ -718,8 +723,14 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
|
||||
arch.trackItem(snItem, oldNode, n, is, time.Since(start))
|
||||
})
|
||||
if err != nil {
|
||||
err = arch.error(join(snPath, name), err)
|
||||
if err == nil {
|
||||
// ignore error
|
||||
continue
|
||||
}
|
||||
return futureNode{}, 0, err
|
||||
}
|
||||
|
||||
nodes = append(nodes, fn)
|
||||
}
|
||||
|
||||
|
||||
@@ -174,12 +174,11 @@ func TestArchiverSaveFileReaderFS(t *testing.T) {
|
||||
|
||||
ts := time.Now()
|
||||
filename := "xx"
|
||||
readerFs := &fs.Reader{
|
||||
ModTime: ts,
|
||||
Mode: 0123,
|
||||
Name: filename,
|
||||
ReadCloser: io.NopCloser(strings.NewReader(test.Data)),
|
||||
}
|
||||
readerFs, err := fs.NewReader(filename, io.NopCloser(strings.NewReader(test.Data)), fs.ReaderOptions{
|
||||
ModTime: ts,
|
||||
Mode: 0123,
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
node, stats := saveFile(t, repo, filename, readerFs)
|
||||
|
||||
@@ -288,13 +287,11 @@ func TestArchiverSaveReaderFS(t *testing.T) {
|
||||
|
||||
ts := time.Now()
|
||||
filename := "xx"
|
||||
readerFs := &fs.Reader{
|
||||
ModTime: ts,
|
||||
Mode: 0123,
|
||||
Name: filename,
|
||||
ReadCloser: io.NopCloser(strings.NewReader(test.Data)),
|
||||
}
|
||||
|
||||
readerFs, err := fs.NewReader(filename, io.NopCloser(strings.NewReader(test.Data)), fs.ReaderOptions{
|
||||
ModTime: ts,
|
||||
Mode: 0123,
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
arch := New(repo, readerFs, Options{})
|
||||
arch.Error = func(item string, err error) error {
|
||||
t.Errorf("archiver error for %v: %v", item, err)
|
||||
@@ -1849,8 +1846,8 @@ func TestArchiverParent(t *testing.T) {
|
||||
func TestArchiverErrorReporting(t *testing.T) {
|
||||
ignoreErrorForBasename := func(basename string) ErrorFunc {
|
||||
return func(item string, err error) error {
|
||||
if filepath.Base(item) == "targetfile" {
|
||||
t.Logf("ignoring error for targetfile: %v", err)
|
||||
if filepath.Base(item) == basename {
|
||||
t.Logf("ignoring error for %v: %v", basename, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1873,12 +1870,13 @@ func TestArchiverErrorReporting(t *testing.T) {
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
src TestDir
|
||||
want TestDir
|
||||
prepare func(t testing.TB)
|
||||
errFn ErrorFunc
|
||||
mustError bool
|
||||
name string
|
||||
targets []string
|
||||
src TestDir
|
||||
want TestDir
|
||||
prepare func(t testing.TB)
|
||||
errFn ErrorFunc
|
||||
errStr []string
|
||||
}{
|
||||
{
|
||||
name: "no-error",
|
||||
@@ -1891,8 +1889,8 @@ func TestArchiverErrorReporting(t *testing.T) {
|
||||
src: TestDir{
|
||||
"targetfile": TestFile{Content: "foobar"},
|
||||
},
|
||||
prepare: chmodUnreadable("targetfile"),
|
||||
mustError: true,
|
||||
prepare: chmodUnreadable("targetfile"),
|
||||
errStr: []string{"open targetfile: permission denied"},
|
||||
},
|
||||
{
|
||||
name: "file-unreadable-ignore-error",
|
||||
@@ -1913,8 +1911,8 @@ func TestArchiverErrorReporting(t *testing.T) {
|
||||
"targetfile": TestFile{Content: "foobar"},
|
||||
},
|
||||
},
|
||||
prepare: chmodUnreadable("subdir/targetfile"),
|
||||
mustError: true,
|
||||
prepare: chmodUnreadable("subdir/targetfile"),
|
||||
errStr: []string{"open subdir/targetfile: permission denied"},
|
||||
},
|
||||
{
|
||||
name: "file-subdir-unreadable-ignore-error",
|
||||
@@ -1932,6 +1930,20 @@ func TestArchiverErrorReporting(t *testing.T) {
|
||||
prepare: chmodUnreadable("subdir/targetfile"),
|
||||
errFn: ignoreErrorForBasename("targetfile"),
|
||||
},
|
||||
{
|
||||
name: "parent-dir-missing",
|
||||
targets: []string{"subdir/missing"},
|
||||
src: TestDir{},
|
||||
errStr: []string{"stat subdir: no such file or directory", "CreateFile subdir: The system cannot find the file specified"},
|
||||
},
|
||||
{
|
||||
name: "parent-dir-missing-filtered",
|
||||
targets: []string{"targetfile", "subdir/missing"},
|
||||
src: TestDir{
|
||||
"targetfile": TestFile{Content: "foobar"},
|
||||
},
|
||||
errFn: ignoreErrorForBasename("subdir"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -1951,14 +1963,21 @@ func TestArchiverErrorReporting(t *testing.T) {
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch.Error = test.errFn
|
||||
|
||||
_, snapshotID, _, err := arch.Snapshot(ctx, []string{"."}, SnapshotOptions{Time: time.Now()})
|
||||
if test.mustError {
|
||||
if err != nil {
|
||||
t.Logf("found expected error (%v), skipping further checks", err)
|
||||
return
|
||||
target := test.targets
|
||||
if len(target) == 0 {
|
||||
target = []string{"."}
|
||||
}
|
||||
_, snapshotID, _, err := arch.Snapshot(ctx, target, SnapshotOptions{Time: time.Now()})
|
||||
if test.errStr != nil {
|
||||
// check if any of the expected errors are contained in the error message
|
||||
for _, errStr := range test.errStr {
|
||||
if strings.Contains(err.Error(), errStr) {
|
||||
t.Logf("found expected error (%v)", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf("expected error not returned by archiver")
|
||||
t.Fatalf("expected error (%v) not returned by archiver, got (%v)", test.errStr, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -43,5 +43,12 @@ func isMacENOTTY(err error) bool {
|
||||
|
||||
// set file to readonly
|
||||
func setFileReadonly(f string, mode os.FileMode) error {
|
||||
return os.Chmod(f, mode&^0222)
|
||||
err := os.Chmod(f, mode&^0222)
|
||||
|
||||
// ignore the error if the FS does not support setting this mode (e.g. CIFS with gvfs on Linux)
|
||||
if err != nil && errors.Is(err, errors.ErrUnsupported) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (b *Backend) IsPermanentError(err error) bool {
|
||||
|
||||
var rerr *restError
|
||||
if errors.As(err, &rerr) {
|
||||
if rerr.StatusCode == http.StatusRequestedRangeNotSatisfiable || rerr.StatusCode == http.StatusUnauthorized || rerr.StatusCode == http.StatusForbidden {
|
||||
if rerr.StatusCode == http.StatusRequestedRangeNotSatisfiable || rerr.StatusCode == http.StatusUnauthorized || rerr.StatusCode == http.StatusForbidden || rerr.StatusCode == http.StatusInsufficientStorage {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type Config struct {
|
||||
|
||||
EnableRestore bool `option:"enable-restore" help:"restore objects from GLACIER or DEEP_ARCHIVE storage classes (default: false, requires \"s3-restore\" feature flag)"`
|
||||
RestoreDays int `option:"restore-days" help:"lifetime in days of restored object (default: 7)"`
|
||||
RestoreTimeout time.Duration `option:"restore-timeout" help:"maximum time to wait for objects transition (default: 1d)"`
|
||||
RestoreTimeout time.Duration `option:"restore-timeout" help:"maximum time to wait for objects transition (default: 24h)"`
|
||||
RestoreTier string `option:"restore-tier" help:"Retrieval tier at which the restore will be processed. (Standard, Bulk or Expedited) (default: Standard)"`
|
||||
|
||||
Connections uint `option:"connections" help:"set a limit for the number of concurrent connections (default: 5)"`
|
||||
|
||||
@@ -293,7 +293,7 @@ type errorBackend struct {
|
||||
func (b errorBackend) Load(ctx context.Context, h backend.Handle, length int, offset int64, consumer func(rd io.Reader) error) error {
|
||||
return b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
|
||||
if b.ProduceErrors {
|
||||
return consumer(errorReadCloser{rd})
|
||||
return consumer(errorReadCloser{Reader: rd})
|
||||
}
|
||||
return consumer(rd)
|
||||
})
|
||||
@@ -301,12 +301,21 @@ func (b errorBackend) Load(ctx context.Context, h backend.Handle, length int, of
|
||||
|
||||
type errorReadCloser struct {
|
||||
io.Reader
|
||||
shortenBy int
|
||||
maxErrorOffset int // if 0, the error can be injected at any offset
|
||||
}
|
||||
|
||||
func (erd errorReadCloser) Read(p []byte) (int, error) {
|
||||
n, err := erd.Reader.Read(p)
|
||||
if n > 0 {
|
||||
induceError(p[:n])
|
||||
maxOffset := n
|
||||
if erd.maxErrorOffset > 0 {
|
||||
maxOffset = min(erd.maxErrorOffset, maxOffset)
|
||||
}
|
||||
induceError(p[:maxOffset])
|
||||
}
|
||||
if n > erd.shortenBy {
|
||||
n -= erd.shortenBy
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -320,17 +329,26 @@ func induceError(data []byte) {
|
||||
// errorOnceBackend randomly modifies data when reading a file for the first time.
|
||||
type errorOnceBackend struct {
|
||||
backend.Backend
|
||||
m sync.Map
|
||||
m sync.Map
|
||||
shortenBy int
|
||||
maxErrorOffset int
|
||||
}
|
||||
|
||||
func (b *errorOnceBackend) Load(ctx context.Context, h backend.Handle, length int, offset int64, consumer func(rd io.Reader) error) error {
|
||||
_, isRetry := b.m.LoadOrStore(h, struct{}{})
|
||||
return b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
|
||||
err := b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
|
||||
if !isRetry && h.Type != restic.ConfigFile {
|
||||
return consumer(errorReadCloser{rd})
|
||||
return consumer(errorReadCloser{Reader: rd, shortenBy: b.shortenBy, maxErrorOffset: b.maxErrorOffset})
|
||||
}
|
||||
return consumer(rd)
|
||||
})
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// retry if the consumer returned an error
|
||||
return b.Backend.Load(ctx, h, length, offset, func(rd io.Reader) error {
|
||||
return consumer(rd)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckerModifiedData(t *testing.T) {
|
||||
@@ -368,6 +386,15 @@ func TestCheckerModifiedData(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
// ignore if a backend returns incomplete garbled data on the first try
|
||||
"corruptPartialOnceBackend",
|
||||
&errorOnceBackend{Backend: be, shortenBy: 10, maxErrorOffset: 100},
|
||||
func() {},
|
||||
func(t *testing.T, err error) {
|
||||
test.Assert(t, err == nil, "unexpected error found, got %v", err)
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
checkRepo := repository.TestOpenBackend(t, test.be)
|
||||
|
||||
@@ -19,97 +19,135 @@ import (
|
||||
// be opened once, all subsequent open calls return syscall.EIO. For Lstat(),
|
||||
// the provided FileInfo is returned.
|
||||
type Reader struct {
|
||||
Name string
|
||||
io.ReadCloser
|
||||
items map[string]readerItem
|
||||
}
|
||||
|
||||
// for FileInfo
|
||||
type ReaderOptions struct {
|
||||
Mode os.FileMode
|
||||
ModTime time.Time
|
||||
Size int64
|
||||
|
||||
AllowEmptyFile bool
|
||||
}
|
||||
|
||||
open sync.Once
|
||||
type readerItem struct {
|
||||
open *sync.Once
|
||||
fi *ExtendedFileInfo
|
||||
rc io.ReadCloser
|
||||
allowEmptyFile bool
|
||||
|
||||
children []string
|
||||
}
|
||||
|
||||
// statically ensure that Local implements FS.
|
||||
var _ FS = &Reader{}
|
||||
|
||||
func NewReader(name string, r io.ReadCloser, opts ReaderOptions) (*Reader, error) {
|
||||
items := make(map[string]readerItem)
|
||||
name = readerCleanPath(name)
|
||||
if name == "/" {
|
||||
return nil, fmt.Errorf("invalid filename specified")
|
||||
}
|
||||
|
||||
isFile := true
|
||||
for {
|
||||
if isFile {
|
||||
fi := &ExtendedFileInfo{
|
||||
Name: path.Base(name),
|
||||
Mode: opts.Mode,
|
||||
ModTime: opts.ModTime,
|
||||
Size: opts.Size,
|
||||
}
|
||||
items[name] = readerItem{
|
||||
open: &sync.Once{},
|
||||
fi: fi,
|
||||
rc: r,
|
||||
allowEmptyFile: opts.AllowEmptyFile,
|
||||
}
|
||||
isFile = false
|
||||
} else {
|
||||
fi := &ExtendedFileInfo{
|
||||
Name: path.Base(name),
|
||||
Mode: os.ModeDir | 0755,
|
||||
ModTime: opts.ModTime,
|
||||
Size: 0,
|
||||
}
|
||||
items[name] = readerItem{
|
||||
fi: fi,
|
||||
// keep the children set during the previous iteration
|
||||
children: items[name].children,
|
||||
}
|
||||
}
|
||||
|
||||
parent := path.Dir(name)
|
||||
if parent == name {
|
||||
break
|
||||
}
|
||||
// add the current file to the children of the parent directory
|
||||
item := items[parent]
|
||||
item.children = append(item.children, path.Base(name))
|
||||
items[parent] = item
|
||||
|
||||
name = parent
|
||||
}
|
||||
return &Reader{
|
||||
items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readerCleanPath(name string) string {
|
||||
return path.Clean("/" + name)
|
||||
}
|
||||
|
||||
// VolumeName returns leading volume name, for the Reader file system it's
|
||||
// always the empty string.
|
||||
func (fs *Reader) VolumeName(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (fs *Reader) fi() *ExtendedFileInfo {
|
||||
return &ExtendedFileInfo{
|
||||
Name: fs.Name,
|
||||
Mode: fs.Mode,
|
||||
ModTime: fs.ModTime,
|
||||
Size: fs.Size,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *Reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
|
||||
if flag & ^(O_RDONLY|O_NOFOLLOW) != 0 {
|
||||
return nil, pathError("open", name,
|
||||
fmt.Errorf("invalid combination of flags 0x%x", flag))
|
||||
}
|
||||
|
||||
switch name {
|
||||
case fs.Name:
|
||||
fs.open.Do(func() {
|
||||
f = newReaderFile(fs.ReadCloser, fs.fi(), fs.AllowEmptyFile)
|
||||
name = readerCleanPath(name)
|
||||
item, ok := fs.items[name]
|
||||
if !ok {
|
||||
return nil, pathError("open", name, syscall.ENOENT)
|
||||
}
|
||||
|
||||
// Check if the path matches our target file
|
||||
if item.rc != nil {
|
||||
item.open.Do(func() {
|
||||
f = newReaderFile(item.rc, item.fi, item.allowEmptyFile)
|
||||
})
|
||||
|
||||
if f == nil {
|
||||
return nil, pathError("open", name, syscall.EIO)
|
||||
}
|
||||
|
||||
return f, nil
|
||||
case "/", ".":
|
||||
f = fakeDir{
|
||||
entries: []string{fs.fi().Name},
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
return nil, pathError("open", name, syscall.ENOENT)
|
||||
f = fakeDir{
|
||||
fakeFile: fakeFile{
|
||||
fi: item.fi,
|
||||
},
|
||||
entries: slices.Clone(item.children),
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Lstat returns the FileInfo structure describing the named file.
|
||||
// If the file is a symbolic link, the returned FileInfo
|
||||
// describes the symbolic link. Lstat makes no attempt to follow the link.
|
||||
// If there is an error, it will be of type *os.PathError.
|
||||
func (fs *Reader) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
getDirInfo := func(name string) *ExtendedFileInfo {
|
||||
return &ExtendedFileInfo{
|
||||
Name: fs.Base(name),
|
||||
Size: 0,
|
||||
Mode: os.ModeDir | 0755,
|
||||
ModTime: time.Now(),
|
||||
}
|
||||
name = readerCleanPath(name)
|
||||
item, ok := fs.items[name]
|
||||
if !ok {
|
||||
return nil, pathError("lstat", name, os.ErrNotExist)
|
||||
}
|
||||
|
||||
switch name {
|
||||
case fs.Name:
|
||||
return fs.fi(), nil
|
||||
case "/", ".":
|
||||
return getDirInfo(name), nil
|
||||
}
|
||||
|
||||
dir := fs.Dir(fs.Name)
|
||||
for {
|
||||
if dir == "/" || dir == "." {
|
||||
break
|
||||
}
|
||||
if name == dir {
|
||||
return getDirInfo(name), nil
|
||||
}
|
||||
dir = fs.Dir(dir)
|
||||
}
|
||||
|
||||
return nil, pathError("lstat", name, os.ErrNotExist)
|
||||
return item.fi, nil
|
||||
}
|
||||
|
||||
// Join joins any number of path elements into a single path, adding a
|
||||
@@ -137,7 +175,7 @@ func (fs *Reader) IsAbs(_ string) bool {
|
||||
//
|
||||
// For the Reader, all paths are absolute.
|
||||
func (fs *Reader) Abs(p string) (string, error) {
|
||||
return path.Clean(p), nil
|
||||
return readerCleanPath(p), nil
|
||||
}
|
||||
|
||||
// Clean returns the cleaned path. For details, see filepath.Clean.
|
||||
|
||||
@@ -17,19 +17,11 @@ import (
|
||||
|
||||
func verifyFileContentOpenFile(t testing.TB, fs FS, filename string, want []byte) {
|
||||
f, err := fs.OpenFile(filename, O_RDONLY, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
|
||||
buf, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
test.OK(t, f.Close())
|
||||
|
||||
if !cmp.Equal(want, buf) {
|
||||
t.Error(cmp.Diff(want, buf))
|
||||
@@ -38,19 +30,11 @@ func verifyFileContentOpenFile(t testing.TB, fs FS, filename string, want []byte
|
||||
|
||||
func verifyDirectoryContents(t testing.TB, fs FS, dir string, want []string) {
|
||||
f, err := fs.OpenFile(dir, O_RDONLY, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
|
||||
entries, err := f.Readdirnames(-1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
test.OK(t, f.Close())
|
||||
|
||||
sort.Strings(want)
|
||||
sort.Strings(entries)
|
||||
@@ -69,7 +53,7 @@ func checkFileInfo(t testing.TB, fi *ExtendedFileInfo, filename string, modtime
|
||||
t.Errorf("Mode has wrong value, want 0%o, got 0%o", mode, fi.Mode)
|
||||
}
|
||||
|
||||
if !modtime.Equal(time.Time{}) && !fi.ModTime.Equal(modtime) {
|
||||
if !fi.ModTime.Equal(modtime) {
|
||||
t.Errorf("ModTime has wrong value, want %v, got %v", modtime, fi.ModTime)
|
||||
}
|
||||
|
||||
@@ -82,40 +66,48 @@ func checkFileInfo(t testing.TB, fi *ExtendedFileInfo, filename string, modtime
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSReader(t *testing.T) {
|
||||
data := test.Random(55, 1<<18+588)
|
||||
now := time.Now()
|
||||
filename := "foobar"
|
||||
type fsTest []struct {
|
||||
name string
|
||||
f func(t *testing.T, fs FS)
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
f func(t *testing.T, fs FS)
|
||||
}{
|
||||
func createReadDirTest(fpath, filename string) fsTest {
|
||||
return fsTest{
|
||||
{
|
||||
name: "Readdirnames-slash",
|
||||
name: "Readdirnames-slash-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
verifyDirectoryContents(t, fs, "/", []string{filename})
|
||||
verifyDirectoryContents(t, fs, "/"+fpath, []string{filename})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Readdirnames-current",
|
||||
name: "Readdirnames-current-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
verifyDirectoryContents(t, fs, ".", []string{filename})
|
||||
verifyDirectoryContents(t, fs, path.Clean(fpath), []string{filename})
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createFileTest(filename string, now time.Time, data []byte) fsTest {
|
||||
return fsTest{
|
||||
{
|
||||
name: "file/OpenFile",
|
||||
f: func(t *testing.T, fs FS) {
|
||||
verifyFileContentOpenFile(t, fs, filename, data)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "file/Open-error-not-exist",
|
||||
f: func(t *testing.T, fs FS) {
|
||||
_, err := fs.OpenFile(filename+"/other", O_RDONLY, false)
|
||||
test.Assert(t, errors.Is(err, os.ErrNotExist), "unexpected error, got %v, expected %v", err, os.ErrNotExist)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "file/Lstat",
|
||||
f: func(t *testing.T, fs FS) {
|
||||
fi, err := fs.Lstat(filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
|
||||
checkFileInfo(t, fi, filename, now, 0644, false)
|
||||
},
|
||||
@@ -123,91 +115,113 @@ func TestFSReader(t *testing.T) {
|
||||
{
|
||||
name: "file/Stat",
|
||||
f: func(t *testing.T, fs FS) {
|
||||
f, err := fs.OpenFile(filename, O_RDONLY, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fi := fsOpenAndStat(t, fs, filename, true)
|
||||
checkFileInfo(t, fi, filename, now, 0644, false)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dir/Lstat-slash",
|
||||
f: func(t *testing.T, fs FS) {
|
||||
fi, err := fs.Lstat("/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true)
|
||||
func createDirTest(fpath string, now time.Time) fsTest {
|
||||
return fsTest{
|
||||
{
|
||||
name: "dir/Lstat-slash-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
fi, err := fs.Lstat("/" + fpath)
|
||||
test.OK(t, err)
|
||||
|
||||
checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dir/Lstat-current",
|
||||
name: "dir/Lstat-current-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
fi, err := fs.Lstat(".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fi, err := fs.Lstat("./" + fpath)
|
||||
test.OK(t, err)
|
||||
|
||||
checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true)
|
||||
checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dir/Lstat-error-not-exist",
|
||||
name: "dir/Lstat-error-not-exist-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
_, err := fs.Lstat("other")
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := fs.Lstat(fpath + "/other")
|
||||
test.Assert(t, errors.Is(err, os.ErrNotExist), "unexpected error, got %v, expected %v", err, os.ErrNotExist)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dir/Open-slash",
|
||||
name: "dir/Open-slash-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
fi, err := fs.Lstat("/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true)
|
||||
fi := fsOpenAndStat(t, fs, "/"+fpath, false)
|
||||
checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dir/Open-current",
|
||||
name: "dir/Open-current-" + fpath,
|
||||
f: func(t *testing.T, fs FS) {
|
||||
fi, err := fs.Lstat(".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true)
|
||||
fi := fsOpenAndStat(t, fs, "./"+fpath, false)
|
||||
checkFileInfo(t, fi, "/"+fpath, now, os.ModeDir|0755, true)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
fs := &Reader{
|
||||
Name: filename,
|
||||
ReadCloser: io.NopCloser(bytes.NewReader(data)),
|
||||
func fsOpenAndStat(t *testing.T, fs FS, fpath string, metadataOnly bool) *ExtendedFileInfo {
|
||||
f, err := fs.OpenFile(fpath, O_RDONLY, metadataOnly)
|
||||
test.OK(t, err)
|
||||
|
||||
fi, err := f.Stat()
|
||||
test.OK(t, err)
|
||||
test.OK(t, f.Close())
|
||||
return fi
|
||||
}
|
||||
|
||||
func TestFSReader(t *testing.T) {
|
||||
data := test.Random(55, 1<<18+588)
|
||||
now := time.Now()
|
||||
filename := "foobar"
|
||||
|
||||
tests := createReadDirTest("", filename)
|
||||
tests = append(tests, createFileTest(filename, now, data)...)
|
||||
tests = append(tests, createDirTest("", now)...)
|
||||
|
||||
for _, tst := range tests {
|
||||
fs, err := NewReader(filename, io.NopCloser(bytes.NewReader(data)), ReaderOptions{
|
||||
Mode: 0644,
|
||||
Size: int64(len(data)),
|
||||
ModTime: now,
|
||||
}
|
||||
})
|
||||
test.OK(t, err)
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.f(t, fs)
|
||||
t.Run(tst.name, func(t *testing.T) {
|
||||
tst.f(t, fs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSReaderNested(t *testing.T) {
|
||||
data := test.Random(55, 1<<18+588)
|
||||
now := time.Now()
|
||||
filename := "foo/sub/bar"
|
||||
|
||||
tests := createReadDirTest("", "foo")
|
||||
tests = append(tests, createReadDirTest("foo", "sub")...)
|
||||
tests = append(tests, createReadDirTest("foo/sub", "bar")...)
|
||||
tests = append(tests, createFileTest(filename, now, data)...)
|
||||
tests = append(tests, createDirTest("", now)...)
|
||||
tests = append(tests, createDirTest("foo", now)...)
|
||||
tests = append(tests, createDirTest("foo/sub", now)...)
|
||||
|
||||
for _, tst := range tests {
|
||||
fs, err := NewReader(filename, io.NopCloser(bytes.NewReader(data)), ReaderOptions{
|
||||
Mode: 0644,
|
||||
Size: int64(len(data)),
|
||||
ModTime: now,
|
||||
})
|
||||
test.OK(t, err)
|
||||
|
||||
t.Run(tst.name, func(t *testing.T) {
|
||||
tst.f(t, fs)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -230,29 +244,24 @@ func TestFSReaderDir(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fs := &Reader{
|
||||
Name: test.filename,
|
||||
ReadCloser: io.NopCloser(bytes.NewReader(data)),
|
||||
|
||||
for _, tst := range tests {
|
||||
t.Run(tst.name, func(t *testing.T) {
|
||||
fs, err := NewReader(tst.filename, io.NopCloser(bytes.NewReader(data)), ReaderOptions{
|
||||
Mode: 0644,
|
||||
Size: int64(len(data)),
|
||||
ModTime: now,
|
||||
}
|
||||
|
||||
dir := path.Dir(fs.Name)
|
||||
})
|
||||
test.OK(t, err)
|
||||
dir := path.Dir(tst.filename)
|
||||
for {
|
||||
if dir == "/" || dir == "." {
|
||||
break
|
||||
}
|
||||
|
||||
fi, err := fs.Lstat(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
|
||||
checkFileInfo(t, fi, dir, time.Time{}, os.ModeDir|0755, true)
|
||||
checkFileInfo(t, fi, dir, now, os.ModeDir|0755, true)
|
||||
|
||||
dir = path.Dir(dir)
|
||||
}
|
||||
@@ -285,40 +294,30 @@ func TestFSReaderMinFileSize(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fs := &Reader{
|
||||
Name: "testfile",
|
||||
ReadCloser: io.NopCloser(strings.NewReader(test.data)),
|
||||
for _, tst := range tests {
|
||||
t.Run(tst.name, func(t *testing.T) {
|
||||
fs, err := NewReader("testfile", io.NopCloser(strings.NewReader(tst.data)), ReaderOptions{
|
||||
Mode: 0644,
|
||||
ModTime: time.Now(),
|
||||
AllowEmptyFile: test.allowEmpty,
|
||||
}
|
||||
|
||||
AllowEmptyFile: tst.allowEmpty,
|
||||
})
|
||||
test.OK(t, err)
|
||||
f, err := fs.OpenFile("testfile", O_RDONLY, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
|
||||
buf, err := io.ReadAll(f)
|
||||
if test.readMustErr {
|
||||
if tst.readMustErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected error not found, got nil")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.OK(t, err)
|
||||
}
|
||||
|
||||
if string(buf) != test.data {
|
||||
t.Fatalf("wrong data returned, want %q, got %q", test.data, string(buf))
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if string(buf) != tst.data {
|
||||
t.Fatalf("wrong data returned, want %q, got %q", tst.data, string(buf))
|
||||
}
|
||||
test.OK(t, f.Close())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,8 @@ func handleXattrErr(err error) error {
|
||||
|
||||
case *xattr.Error:
|
||||
// On Linux, xattr calls on files in an SMB/CIFS mount can return
|
||||
// ENOATTR instead of ENOTSUP.
|
||||
switch e.Err {
|
||||
case syscall.ENOTSUP, xattr.ENOATTR:
|
||||
// ENOATTR instead of ENOTSUP. BSD can return EOPNOTSUPP.
|
||||
if e.Err == syscall.ENOTSUP || e.Err == syscall.EOPNOTSUPP || e.Err == xattr.ENOATTR {
|
||||
return nil
|
||||
}
|
||||
return errors.WithStack(e)
|
||||
|
||||
@@ -88,10 +88,14 @@ func checkPackInner(ctx context.Context, r *Repository, id restic.ID, blobs []re
|
||||
// calculate hash on-the-fly while reading the pack and capture pack header
|
||||
var hash restic.ID
|
||||
var hdrBuf []byte
|
||||
// must use a separate slice from `errs` here as we're only interested in the last retry
|
||||
var blobErrors []error
|
||||
h := backend.Handle{Type: backend.PackFile, Name: id.String()}
|
||||
err := r.be.Load(ctx, h, int(size), 0, func(rd io.Reader) error {
|
||||
hrd := hashing.NewReader(rd, sha256.New())
|
||||
bufRd.Reset(hrd)
|
||||
// reset blob errors for each retry
|
||||
blobErrors = nil
|
||||
|
||||
it := newPackBlobIterator(id, newBufReader(bufRd), 0, blobs, r.Key(), dec)
|
||||
for {
|
||||
@@ -108,7 +112,7 @@ func checkPackInner(ctx context.Context, r *Repository, id restic.ID, blobs []re
|
||||
debug.Log(" check blob %v: %v", val.Handle.ID, val.Handle)
|
||||
if val.Err != nil {
|
||||
debug.Log(" error verifying blob %v: %v", val.Handle.ID, val.Err)
|
||||
errs = append(errs, errors.Errorf("blob %v: %v", val.Handle.ID, val.Err))
|
||||
blobErrors = append(blobErrors, errors.Errorf("blob %v: %v", val.Handle.ID, val.Err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +138,7 @@ func checkPackInner(ctx context.Context, r *Repository, id restic.ID, blobs []re
|
||||
hash = restic.IDFromHash(hrd.Sum(nil))
|
||||
return nil
|
||||
})
|
||||
errs = append(errs, blobErrors...)
|
||||
if err != nil {
|
||||
var e *partialReadError
|
||||
isPartialReadError := errors.As(err, &e)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
)
|
||||
@@ -52,7 +51,7 @@ func nextNumber(input string) (num int, rest string, err error) {
|
||||
}
|
||||
|
||||
for i, s := range input {
|
||||
if !unicode.IsNumber(s) {
|
||||
if s < '0' || s > '9' {
|
||||
rest = input[i:]
|
||||
break
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ func TestNextNumber(t *testing.T) {
|
||||
{
|
||||
input: "5d ", num: 5, rest: "d ",
|
||||
},
|
||||
{
|
||||
input: "5", num: 5, rest: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -78,6 +81,7 @@ func TestParseDuration(t *testing.T) {
|
||||
{input: "2w", err: true},
|
||||
{input: "1y4m3w1d", err: true},
|
||||
{input: "s", err: true},
|
||||
{input: "\xdf\x80", err: true}, // NKO DIGIT ZERO; we want ASCII digits
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -908,13 +908,12 @@ func TestRestorerSparseFiles(t *testing.T) {
|
||||
|
||||
var zeros [1<<20 + 13]byte
|
||||
|
||||
target := &fs.Reader{
|
||||
Mode: 0600,
|
||||
Name: "/zeros",
|
||||
ReadCloser: io.NopCloser(bytes.NewReader(zeros[:])),
|
||||
}
|
||||
target, err := fs.NewReader("/zeros", io.NopCloser(bytes.NewReader(zeros[:])), fs.ReaderOptions{
|
||||
Mode: 0600,
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
sc := archiver.NewScanner(target)
|
||||
err := sc.Scan(context.TODO(), []string{"/zeros"})
|
||||
err = sc.Scan(context.TODO(), []string{"/zeros"})
|
||||
rtest.OK(t, err)
|
||||
|
||||
arch := archiver.New(repo, target, archiver.Options{})
|
||||
|
||||
@@ -91,6 +91,8 @@ func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTree
|
||||
if err == ErrSkipNode {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
err = walk(ctx, repo, p, *node.Subtree, subtree, visitor)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
// TestTree is used to construct a list of trees for testing the walker.
|
||||
@@ -93,12 +94,12 @@ func (t TreeMap) Connections() uint {
|
||||
|
||||
// checkFunc returns a function suitable for walking the tree to check
|
||||
// something, and a function which will check the final result.
|
||||
type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB))
|
||||
type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error))
|
||||
|
||||
// checkItemOrder ensures that the order of the 'path' arguments is the one passed in as 'want'.
|
||||
func checkItemOrder(want []string) checkFunc {
|
||||
pos := 0
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
|
||||
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
|
||||
if err != nil {
|
||||
t.Errorf("error walking %v: %v", path, err)
|
||||
@@ -121,7 +122,8 @@ func checkItemOrder(want []string) checkFunc {
|
||||
return walker(restic.ID{}, "leave: "+path, nil, nil)
|
||||
}
|
||||
|
||||
final = func(t testing.TB) {
|
||||
final = func(t testing.TB, err error) {
|
||||
rtest.OK(t, err)
|
||||
if pos != len(want) {
|
||||
t.Errorf("not enough items returned, want %d, got %d", len(want), pos)
|
||||
}
|
||||
@@ -134,7 +136,7 @@ func checkItemOrder(want []string) checkFunc {
|
||||
// checkParentTreeOrder ensures that the order of the 'parentID' arguments is the one passed in as 'want'.
|
||||
func checkParentTreeOrder(want []string) checkFunc {
|
||||
pos := 0
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
|
||||
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
|
||||
if err != nil {
|
||||
t.Errorf("error walking %v: %v", path, err)
|
||||
@@ -153,7 +155,8 @@ func checkParentTreeOrder(want []string) checkFunc {
|
||||
return nil
|
||||
}
|
||||
|
||||
final = func(t testing.TB) {
|
||||
final = func(t testing.TB, err error) {
|
||||
rtest.OK(t, err)
|
||||
if pos != len(want) {
|
||||
t.Errorf("not enough items returned, want %d, got %d", len(want), pos)
|
||||
}
|
||||
@@ -168,7 +171,7 @@ func checkParentTreeOrder(want []string) checkFunc {
|
||||
func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
|
||||
var pos int
|
||||
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) {
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
|
||||
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
|
||||
if err != nil {
|
||||
t.Errorf("error walking %v: %v", path, err)
|
||||
@@ -196,7 +199,8 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
|
||||
return walker(restic.ID{}, "leave: "+path, nil, nil)
|
||||
}
|
||||
|
||||
final = func(t testing.TB) {
|
||||
final = func(t testing.TB, err error) {
|
||||
rtest.OK(t, err)
|
||||
if pos != len(wantPaths) {
|
||||
t.Errorf("wrong number of paths returned, want %d, got %d", len(wantPaths), pos)
|
||||
}
|
||||
@@ -206,6 +210,32 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func checkErrorReturned(errForPath string) checkFunc {
|
||||
expectedErr := fmt.Errorf("error for %v", errForPath)
|
||||
|
||||
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
|
||||
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
|
||||
if path == errForPath {
|
||||
return expectedErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
leaveDir = func(path string) error {
|
||||
return walker(restic.ID{}, "leave: "+path, nil, nil)
|
||||
}
|
||||
|
||||
final = func(t testing.TB, err error) {
|
||||
if err == nil {
|
||||
t.Errorf("expected error for %v, got nil", errForPath)
|
||||
}
|
||||
rtest.Assert(t, err == expectedErr, "expected error for %v, got %v", errForPath, err)
|
||||
}
|
||||
|
||||
return walker, leaveDir, final
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalker(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tree TestTree
|
||||
@@ -427,6 +457,21 @@ func TestWalker(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
tree: TestTree{
|
||||
"subdir1": TestTree{
|
||||
"file": TestFile{},
|
||||
},
|
||||
"subdir2": TestTree{},
|
||||
},
|
||||
checks: []checkFunc{
|
||||
checkErrorReturned("/subdir1"),
|
||||
checkErrorReturned("/subdir2"),
|
||||
checkErrorReturned("/subdir1/file"),
|
||||
checkErrorReturned("leave: /subdir1"),
|
||||
checkErrorReturned("leave: /subdir2"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -442,10 +487,7 @@ func TestWalker(t *testing.T) {
|
||||
ProcessNode: fn,
|
||||
LeaveDir: leaveDir,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
last(t)
|
||||
last(t, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user