Compare commits

..

2 Commits

Author SHA1 Message Date
Frederik Ring
88d01bcb18 Use feature branch for package sftp 2025-02-27 19:51:12 +01:00
Frederik Ring
3a395ba296 Info on filenames is missing on errors when deleting files 2025-02-27 13:58:32 +01:00
6 changed files with 149 additions and 235 deletions

View File

@@ -9,7 +9,7 @@ nav_order: 2
Backup targets, schedule and retention are configured using environment variables.
{: .note }
As per established convention, you can use any environment variable key from below with a `_FILE` suffix in order to load the value from a file instead.
You can use any environment variable from below also with a `_FILE` suffix to be able to load the value from a file.
This is typically useful when using [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/) or similar.
Note that secrets will not be trimmed of leading or trailing whitespace.
@@ -17,14 +17,13 @@ Note that secrets will not be trimmed of leading or trailing whitespace.
In case you encounter double quoted values in your runtime configuration you might still be using an [older version of `docker-compose`][compose-issue].
You can work around this by either updating `docker-compose` or unquoting your configuration values.
You can populate below template according to your requirements and use it as your `env_file`.
The values for each key currently match its default.
You can populate below template according to your requirements and use it as your `env_file`:
{% raw %}
```
########### BACKUP SCHEDULE
# Backups can be run on fixed scheduled that are defined as a cron expression.
# A cron expression represents a set of times, using 5 or 6 space-separated fields.
#
# Field name | Mandatory? | Allowed values | Allowed special characters
@@ -38,14 +37,10 @@ The values for each key currently match its default.
#
# Month and Day-of-week field values are case insensitive.
# "SUN", "Sun", and "sun" are equally accepted.
# If no value is set, `@daily` will be used.
# If you do not want the cron to ever run, use `0 0 5 31 2 ?`.
# Refer to sites like <https://crontab.guru> for help.
# If no value is set, `@daily` will be used, which runs every
# day at midnight.
# BACKUP_CRON_EXPRESSION="@daily"
# ---
# BACKUP_CRON_EXPRESSION="0 2 * * *"
# The compression algorithm used in conjunction with tar.
# Valid options are: "gz" (Gzip), "zst" (Zstd) or "none" (tar only).
@@ -53,19 +48,15 @@ The values for each key currently match its default.
# BACKUP_COMPRESSION="gz"
# ---
# Parallelism level for "gz" (Gzip) compression.
# Defines how many blocks of data are concurrently processed.
# Higher values result in faster compression. No effect on decompression
# Default = 1. Setting this to 0 will use all available threads.
# GZIP_PARALLELISM="1"
# GZIP_PARALLELISM=1
# ---
# The desired name of the backup file including the extension.
# Format verbs will be replaced as in `strftime`. Omitting all verbs
# The name of the backup file including the extension.
# Format verbs will be replaced as in `strftime`. Omitting them
# will result in the same filename for every backup run, which means previous
# versions will be overwritten on subsequent runs.
# Extension can be defined literally or via "{{ .Extension }}" template,
@@ -75,8 +66,6 @@ The values for each key currently match its default.
# BACKUP_FILENAME="backup-%Y-%m-%dT%H-%M-%S.{{ .Extension }}"
# ---
# Setting BACKUP_FILENAME_EXPAND to true allows for environment variable
# placeholders in BACKUP_FILENAME, BACKUP_LATEST_SYMLINK and in
# BACKUP_PRUNING_PREFIX that will get expanded at runtime,
@@ -87,15 +76,10 @@ The values for each key currently match its default.
# BACKUP_FILENAME_EXPAND="true"
# ---
# When storing local backups, a symlink to the latest backup can be created
# in case a value is given for this key. This has no effect on remote backups.
# Example: "backup.latest.tar.gz"
# BACKUP_LATEST_SYMLINK=""
# ---
# BACKUP_LATEST_SYMLINK="backup.latest.tar.gz"
# ************************************************************************
# The BACKUP_FROM_SNAPSHOT option has been deprecated and will be removed
@@ -109,285 +93,203 @@ The values for each key currently match its default.
# BACKUP_FROM_SNAPSHOT="false"
# ---
# By default, the `/backup` directory inside the container will be backed up.
# In case you need to use a custom location, set `BACKUP_SOURCES`.
# By default, the contents of the `/backup` directory inside the container
# will be backed up. In case you need to use a custom location, set `BACKUP_SOURCES`.
# Example: "/other/location"
# BACKUP_SOURCES="/other/location"
# BACKUP_SOURCES="/backup"
# ---
# When a value is given, all files in BACKUP_SOURCES whose full path matches the
# When given, all files in BACKUP_SOURCES whose full path matches the given
# regular expression will be excluded from the archive. Regular Expressions
# can be used as from the Go standard library https://pkg.go.dev/regexp
# Example: "\.log$"
# BACKUP_EXCLUDE_REGEXP=""
# ---
# BACKUP_EXCLUDE_REGEXP="\.log$"
# Exclude one or many storage backends from the pruning process.
# Available backends are: S3, WebDAV, SSH, Local, Dropbox, Azure
# E.g. with one backend excluded: BACKUP_SKIP_BACKENDS_FROM_PRUNE=s3
# E.g. with multiple backends excluded: BACKUP_SKIP_BACKENDS_FROM_PRUNE=s3,webdav
# Note: The names of the backends are case insensitive.
# Available backends are: S3, WebDAV, SSH, Local, Dropbox, Azure
# Note: The name of the backends is case insensitive.
# Default: All backends get pruned.
# BACKUP_SKIP_BACKENDS_FROM_PRUNE=""
# BACKUP_SKIP_BACKENDS_FROM_PRUNE=
########### S3 COMPATIBLE STORAGE
########### BACKUP STORAGE
# The name of the remote bucket that should be used for storing backups. If
# this is not set, no remote backups will be stored.
# Example: "backup-bucket"
# AWS_S3_BUCKET_NAME=""
# ---
# AWS_S3_BUCKET_NAME="backup-bucket"
# If you want to store the backup in a non-root location on your bucket
# you can provide a path. The path must not contain a leading slash.
# Example: "my/backup/location"
# AWS_S3_PATH=""
# ---
# AWS_S3_PATH="my/backup/location"
# Define credentials for authenticating against the backup storage and a bucket
# name. Although all of these keys are `AWS`-prefixed, the setup can be used
# with any S3 compatible storage.
# AWS_ACCESS_KEY_ID=""
# AWS_SECRET_ACCESS_KEY=""
# ---
# AWS_ACCESS_KEY_ID="<xxx>"
# AWS_SECRET_ACCESS_KEY="<xxx>"
# Instead of providing static credentials, you can also use IAM instance profiles
# or similar to provide authentication. Some possible configuration options on AWS:
# - EC2: http://169.254.169.254
# - ECS: http://169.254.170.2
# AWS_IAM_ROLE_ENDPOINT=""
# ---
# AWS_IAM_ROLE_ENDPOINT="http://169.254.169.254"
# This is the FQDN of your storage server, e.g. `storage.example.com`.
# If you need to set a specific (non-https) protocol, you will need to use the option below.
# The default value points to the standard AWS S3 endpoint.
# Do not set this when working against AWS S3 (the default value is
# `s3.amazonaws.com`). If you need to set a specific (non-https) protocol, you
# will need to use the option below.
# AWS_ENDPOINT="s3.amazonaws.com"
# AWS_ENDPOINT="storage.example.com"
# ---
# The protocol to be used when communicating with your S3 storage server.
# The protocol to be used when communicating with your storage server.
# Defaults to "https". You can set this to "http" when communicating with
# a different Docker container in the same virtual network for example.
# a different Docker container on the same host for example.
# AWS_ENDPOINT_PROTO="https"
# ---
# Setting this variable to `true` will disable verification of
# SSL certificates for AWS_ENDPOINT. You shouldn't use this unless you use
# self-signed certificates for your remote storage backend. This can only be
# used when AWS_ENDPOINT_PROTO is set to `https`.
# AWS_ENDPOINT_INSECURE="false"
# ---
# AWS_ENDPOINT_INSECURE="true"
# If you wish to use self signed certificates your S3 server, you can pass
# the location of a PEM encoded CA certificate and it will be used for
# validating your certificates. Alternatively, pass a PEM encoded string
# containing the certificate.
# Example: "/path/to/cert.pem"
# validating your certificates.
# Alternatively, pass a PEM encoded string containing the certificate.
# AWS_ENDPOINT_CA_CERT=""
# AWS_ENDPOINT_CA_CERT="/path/to/cert.pem"
# ---
# Setting this variable will change the S3 storage class header.
# Defaults to "STANDARD", you can set this value according to your needs.
# Setting a value for this key will change the S3 storage class header.
# Default behavior is to use the standard class when no value is given.
# Example: "GLACIER"
# AWS_STORAGE_CLASS=""
# ---
# AWS_STORAGE_CLASS="GLACIER"
# Setting this variable will change the S3 default part size for the copy step.
# This value is useful when you want to upload large files.
# NB: While using Scaleway as S3 provider, be aware that the parts counter is set to 1.000.
# NB : While using Scaleway as S3 provider, be aware that the parts counter is set to 1.000.
# While Minio uses a hard coded value to 10.000. As a workaround, try to set a higher value.
# Defaults to "16" (MB) if unset (from minio), you can set this value according to your needs.
# The unit is in MB and an integer.
# AWS_PART_SIZE="16"
# AWS_PART_SIZE=16
########### WEBDAV STORAGE
# You can also backup files to any WebDAV server:
# The URL of the remote WebDAV server
# Example: "https://webdav.example.com"
# WEBDAV_URL=""
# ---
# WEBDAV_URL="https://webdav.example.com"
# The Directory to place the backups to on the WebDAV server.
# If the path is not present on the server it will be created.
# Example: "/my/directory/"
# WEBDAV_PATH=""
# ---
# WEBDAV_PATH="/my/directory/"
# The username for the WebDAV server
# Example: "user"
# WEBDAV_USERNAME=""
# ---
# WEBDAV_USERNAME="user"
# The password for the WebDAV server
# Example: "password"
# WEBDAV_PASSWORD=""
# WEBDAV_PASSWORD="password"
# ---
# Setting this variable to "true" will disable verification of
# Setting this variable to `true` will disable verification of
# SSL certificates for WEBDAV_URL. You shouldn't use this unless you use
# self-signed certificates for your remote storage backend.
# WEBDAV_URL_INSECURE="false"
# WEBDAV_URL_INSECURE="true"
########### SSH/SFTP STORAGE
# You can also backup files to any SSH server:
# The FQDN of the remote SSH server
# Example: "server.local"
# The URL of the remote SSH server
# SSH_HOST_NAME=""
# ---
# SSH_HOST_NAME="server.local"
# The port of the remote SSH server
# Optional variable default value is `22`
# SSH_PORT="22"
# ---
# SSH_PORT=2222
# The Directory to place the backups to on the SSH server.
# Example: "/home/user/backups"
# SSH_REMOTE_PATH=""
# ---
# SSH_REMOTE_PATH="/my/directory/"
# The username for the SSH server
# Example: "user"
# SSH_USER=""
# ---
# SSH_USER="user"
# The password for the SSH server
# Example: "password"
# SSH_PASSWORD=""
# SSH_PASSWORD="password"
# ---
# The private key path in container for SSH server.
# Consumers can mount a file into /root/.ssh/id_rsa (or the respective value)
# in order to have it being used. Non-RSA keys (e.g. ed25519) will also work.
# The private key path in container for SSH server
# Default value: /root/.ssh/id_rsa
# If file is mounted to /root/.ssh/id_rsa path it will be used. Non-RSA keys will
# also work.
# SSH_IDENTITY_FILE="/root/.ssh/id_rsa"
# ---
# The passphrase for the identity file
# The passphrase for the identity file if applicable
# Example: "pass"
# SSH_IDENTITY_PASSPHRASE=""
########### AZURE BLOB STORAGE
# SSH_IDENTITY_PASSPHRASE="pass"
# The credential's account name when using Azure Blob Storage. This has to be
# set when using Azure Blob Storage.
# Example: "account-name"
# AZURE_STORAGE_ACCOUNT_NAME=""
# ---
# AZURE_STORAGE_ACCOUNT_NAME="account-name"
# The credential's primary account key when using Azure Blob Storage. If this
# is not given, the command tries to fall back to using a connection string
# (if given) or a managed identity (if neither is set).
# (if given) or a managed identity (if nothing is given).
# AZURE_STORAGE_PRIMARY_ACCOUNT_KEY=""
# ---
# AZURE_STORAGE_PRIMARY_ACCOUNT_KEY="<xxx>"
# A connection string for accessing Azure Blob Storage. If this
# is not given, the command tries to fall back to using a primary account key
# (if given) or a managed identity (if neither is set).
# (if given) or a managed identity (if nothing is given).
# AZURE_STORAGE_CONNECTION_STRING=""
# ---
# AZURE_STORAGE_CONNECTION_STRING="<xxx>"
# The container name when using Azure Blob Storage.
# Example: "container-name"
# AZURE_STORAGE_CONTAINER_NAME=""
# ---
# AZURE_STORAGE_CONTAINER_NAME="container-name"
# The service endpoint when using Azure Blob Storage. This is a template that
# can be passed the account name as shown in the default value below.
# AZURE_STORAGE_ENDPOINT="https://{{ .AccountName }}.blob.core.windows.net/"
# ---
# Absolute remote path in your Dropbox where the backups shall be stored.
# Note: Use your app's subpath in Dropbox, if it doesn't have global access.
# Consulte the README for further information.
# The access tier when using Azure Blob Storage. Possible values are
# https://github.com/Azure/azure-sdk-for-go/blob/sdk/storage/azblob/v1.3.2/sdk/storage/azblob/internal/generated/zz_constants.go#L14-L30
# Example: "Cold"
# AZURE_STORAGE_ACCESS_TIER=""
# AZURE_STORAGE_ACCESS_TIER="Cold"
########### DROPBOX STORAGE
# Absolute remote path in your Dropbox where the backups shall be stored.
# Note: Use your app's subpath in Dropbox, if it doesn't have global access.
# Consult the README for further information.
# Example: "/my/directory"
# DROPBOX_REMOTE_PATH=""
# ---
# App key and app secret from your app created at https://www.dropbox.com/developers/apps/info
# DROPBOX_APP_KEY=""
# DROPBOX_APP_SECRET=""
# ---
# DROPBOX_REMOTE_PATH="/my/directory"
# Number of concurrent chunked uploads for Dropbox.
# Values above 6 usually result in no enhancements.
# DROPBOX_CONCURRENCY_LEVEL="6"
# ---
# App key and app secret from your app created at https://www.dropbox.com/developers/apps/info
# DROPBOX_APP_KEY=""
# DROPBOX_APP_SECRET=""
# Refresh token to request new short-lived tokens (OAuth2). Consult README to see how to get one.
# DROPBOX_REFRESH_TOKEN=""
########### LOCAL FILE STORAGE
# In addition to storing backups remotely, you can also keep local copies.
# Pass a container-local path to store your backups if needed. You also need to
# mount a local folder or Docker volume into that location (`/archive`
@@ -409,12 +311,10 @@ The values for each key currently match its default.
# for such files, or to configure BACKUP_PRUNING_PREFIX to limit
# removal to certain files.
# Pass zero or a positive integer value to enable automatic rotation of
# old backups. The value declares the number of days for which a backup is kept.
# Define this value to enable automatic rotation of old backups. The value
# declares the number of days for which a backup is kept.
# BACKUP_RETENTION_DAYS="-1"
# ---
# BACKUP_RETENTION_DAYS="7"
# In case the duration a backup takes fluctuates noticeably in your setup
# you can adjust this setting to make sure there are no race conditions
@@ -426,8 +326,6 @@ The values for each key currently match its default.
# BACKUP_PRUNING_LEEWAY="1m"
# ---
# In case your target bucket or directory contains other files than the ones
# managed by this container, you can limit the scope of rotation by setting
# a prefix value. This would usually be the non-parametrized part of your
@@ -435,7 +333,7 @@ The values for each key currently match its default.
# you can set BACKUP_PRUNING_PREFIX to `db-backup-` and make sure
# unrelated files are not affected by the rotation mechanism.
# BACKUP_PRUNING_PREFIX=""
# BACKUP_PRUNING_PREFIX="backup-"
########### BACKUP ENCRYPTION
@@ -444,28 +342,26 @@ The values for each key currently match its default.
# Backups can be encrypted symmetrically using gpg in case a passphrase is given.
# GPG_PASSPHRASE=""
# ---
# GPG_PASSPHRASE="<xxx>"
# Backups can be encrypted asymmetrically using gpg in case publickeys are given.
# You can use pipe syntax to pass a multiline value.
# GPG_PUBLIC_KEY_RING=""
# ---
# GPG_PUBLIC_KEY_RING= |
#-----BEGIN PGP PUBLIC KEY BLOCK-----
#
#D/cIHu6GH/0ghlcUVSbgMg5RRI5QKNNKh04uLAPxr75mKwUg0xPUaWgyyrAChVBi
#...
#-----END PGP PUBLIC KEY BLOCK-----
# Backups can be encrypted symmetrically using age in case a passphrase is given.
# AGE_PASSPHRASE=""
# ---
# AGE_PASSPHRASE="<xxx>"
# Backups can be encrypted asymmetrically using age in case publickeys are given.
# Multiple keys need to be provided as a comma separated list. Right now, this
# supports `age` and `ssh` keys
# AGE_PUBLIC_KEYS=""
# AGE_PUBLIC_KEYS="<xxx>"
########### STOPPING CONTAINERS AND SERVICES DURING BACKUP
@@ -473,17 +369,18 @@ The values for each key currently match its default.
# `docker-volume-backup.stop-during-backup` label. By default, all containers and
# services that are labeled with `true` will be stopped. If you need more fine
# grained control (e.g. when running multiple containers based on this image),
# you can override this default by specifying a different string value here.
# BACKUP_STOP_DURING_BACKUP_LABEL="true"
# you can override this default by specifying a different value here.
# BACKUP_STOP_DURING_BACKUP_LABEL="service1"
# When trying to scale down Docker Swarm services, give up after
# the specified amount of time in case the service has not converged yet.
# In case you need to adjust this timeout, supply a duration
# value as per https://pkg.go.dev/time#ParseDuration to `BACKUP_STOP_SERVICE_TIMEOUT`.
# Defaults to 5 minutes.
# BACKUP_STOP_SERVICE_TIMEOUT="5m"
########### EXECUTING COMMANDS IN CONTAINERS DURING THE BACKUP LIFECYCLE
########### EXECUTING COMMANDS IN CONTAINERS PRE/POST BACKUP
# It is possible to define commands to be run in any container before and after
# a backup is conducted. The commands themselves are defined in labels like
@@ -494,17 +391,15 @@ The values for each key currently match its default.
# is configured to be "true", command execution output will be forwarded to
# the backup container's stdout and stderr.
# EXEC_FORWARD_OUTPUT="false"
# ---
# EXEC_FORWARD_OUTPUT="true"
# Without any further configuration, all commands defined in labels will be
# run before and after a backup. If you need more fine grained control, you
# can use this option to set a label that will be used for narrowing down
# the set of eligible containers. E.g. when setting this to `database`,
# an eligible container will also need to be labeled as `docker-volume-backup.exec-label=database`.
# the set of eligible containers. When set, an eligible container will also need
# to be labeled as `docker-volume-backup.exec-label=database`.
# EXEC_LABEL=""
# EXEC_LABEL="database"
########### NOTIFICATIONS
@@ -515,13 +410,10 @@ The values for each key currently match its default.
# on how to do this can be found in the README. When providing multiple URLs or
# an URL that contains a comma, the values can be URL encoded to avoid ambiguities.
# The following example URL demonstrates how to send an email using the provided SMTP
# The below URL demonstrates how to send an email using the provided SMTP
# configuration and credentials.
# Example: "smtp://username:password@host:587/?fromAddress=sender@example.com&toAddresses=recipient@example.com"
# NOTIFICATION_URLS=""
# ---
# NOTIFICATION_URLS=smtp://username:password@host:587/?fromAddress=sender@example.com&toAddresses=recipient@example.com
# By default, notifications would only be sent out when a backup run fails
# To receive notifications for every run, set `NOTIFICATION_LEVEL` to `info`
@@ -533,9 +425,8 @@ The values for each key currently match its default.
# If you are interfacing with Docker via TCP you can set the Docker host here
# instead of mounting the Docker socket as a volume. This is unset by default.
# Example: "tcp://docker_socket_proxy:2375"
# DOCKER_HOST=""
# DOCKER_HOST="tcp://docker_socket_proxy:2375"
########### LOCK_TIMEOUT
@@ -562,25 +453,20 @@ The values for each key currently match its default.
# The recipient(s) of the notification. Supply a comma separated list
# of addresses if you want to notify multiple recipients. If this is
# not set, no emails will be sent.
# Example: "you@example.com"
# EMAIL_NOTIFICATION_RECIPIENT=""
# EMAIL_NOTIFICATION_RECIPIENT="you@example.com"
# ---
# The "From" header of the sent email. Defaults to `noreply@nohost`.
# The "From" header of the sent email.
# Example: "no-reply@example.com"
# EMAIL_NOTIFICATION_SENDER="noreply@nohost"
# ---
# EMAIL_NOTIFICATION_SENDER="no-reply@example.com"
# Configuration and credentials for the SMTP server to be used.
# EMAIL_SMTP_PORT defaults to 587.
# EMAIL_SMTP_HOST=""
# EMAIL_SMTP_PASSWORD=""
# EMAIL_SMTP_USERNAME=""
# EMAIL_SMTP_PORT="587"
# EMAIL_SMTP_HOST="posteo.de"
# EMAIL_SMTP_PASSWORD="<xxx>"
# EMAIL_SMTP_USERNAME="no-reply@example.com"
# EMAIL_SMTP_PORT="<port>"
```
{% endraw %}

4
go.mod
View File

@@ -17,10 +17,10 @@ require (
github.com/minio/minio-go/v7 v7.0.87
github.com/offen/envconfig v1.5.0
github.com/otiai10/copy v1.14.1
github.com/pkg/sftp v1.13.7
github.com/pkg/sftp v1.13.8-0.20250227140229-584399def273
github.com/robfig/cron/v3 v3.0.1
github.com/studio-b12/gowebdav v0.10.0
golang.org/x/crypto v0.33.0
golang.org/x/crypto v0.35.0
golang.org/x/oauth2 v0.27.0
golang.org/x/sync v0.11.0
mvdan.cc/sh/v3 v3.10.0

32
go.sum
View File

@@ -255,6 +255,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
github.com/pkg/sftp v1.13.8-0.20250227140229-584399def273 h1:v0UIpSUEBdbCrj69QtrhXvpkQbTMYfTjV5KF1v5AJGM=
github.com/pkg/sftp v1.13.8-0.20250227140229-584399def273/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -316,9 +318,15 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -351,6 +359,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -382,6 +393,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.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.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -403,6 +417,10 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.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.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -444,14 +462,23 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -462,7 +489,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -515,6 +545,8 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -96,7 +96,7 @@ func (b *localStorage) Prune(deadline time.Time, pruningPrefix string) (*storage
)
}
if !fi.IsDir() && fi.Mode()&os.ModeSymlink != os.ModeSymlink {
if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
candidates = append(candidates, candidate)
}
}

View File

@@ -163,24 +163,21 @@ func (b *sshStorage) Prune(deadline time.Time, pruningPrefix string) (*storage.P
}
var matches []string
var numCandidates int
for _, candidate := range candidates {
if candidate.IsDir() || !strings.HasPrefix(candidate.Name(), pruningPrefix) {
if !strings.HasPrefix(candidate.Name(), pruningPrefix) {
continue
}
numCandidates++
if candidate.ModTime().Before(deadline) {
matches = append(matches, candidate.Name())
}
}
stats := &storage.PruneStats{
Total: uint(numCandidates),
Total: uint(len(candidates)),
Pruned: uint(len(matches)),
}
pruneErr := b.DoPrune(b.Name(), len(matches), numCandidates, deadline, func() error {
pruneErr := b.DoPrune(b.Name(), len(matches), len(candidates), deadline, func() error {
for _, match := range matches {
p := path.Join(b.DestinationPath, match)
if err := b.sftpClient.Remove(p); err != nil {

View File

@@ -90,25 +90,24 @@ func (b *webDavStorage) Prune(deadline time.Time, pruningPrefix string) (*storag
if err != nil {
return nil, errwrap.Wrap(err, "error looking up candidates from remote storage")
}
var matches []fs.FileInfo
var numCandidates int
var lenCandidates int
for _, candidate := range candidates {
if candidate.IsDir() || !strings.HasPrefix(candidate.Name(), pruningPrefix) {
if !strings.HasPrefix(candidate.Name(), pruningPrefix) {
continue
}
numCandidates++
lenCandidates++
if candidate.ModTime().Before(deadline) {
matches = append(matches, candidate)
}
}
stats := &storage.PruneStats{
Total: uint(numCandidates),
Total: uint(lenCandidates),
Pruned: uint(len(matches)),
}
pruneErr := b.DoPrune(b.Name(), len(matches), numCandidates, deadline, func() error {
pruneErr := b.DoPrune(b.Name(), len(matches), lenCandidates, deadline, func() error {
for _, match := range matches {
if err := b.client.Remove(path.Join(b.DestinationPath, match.Name())); err != nil {
return errwrap.Wrap(err, "error removing file")