You've already forked docker-volume-backup
mirror of
https://github.com/offen/docker-volume-backup.git
synced 2025-10-06 05:36:56 +02:00
Update golangci integration (#637)
* Update golangci integration * Fix newly discovered errcheck complaints * Increase timeout value
This commit is contained in:
41
.github/workflows/golangci-lint.yml
vendored
41
.github/workflows/golangci-lint.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# Optional: allow read access to pull request. Use with `only-new-issues` option.
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
@@ -15,40 +14,12 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.24'
|
||||
cache: false
|
||||
go-version: '1.25'
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
# Require: The version of golangci-lint to use.
|
||||
# When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version.
|
||||
# When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit.
|
||||
version: v1.64
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
#
|
||||
# Note: By default, the `.golangci.yml` file should be at the root of the repository.
|
||||
# The location of the configuration file can be changed by using `--config=`
|
||||
# args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
|
||||
# Optional: if set to true, then all caching functionality will be completely disabled,
|
||||
# takes precedence over all other caching options.
|
||||
# skip-cache: true
|
||||
|
||||
# Optional: if set to true, then the action won't cache or restore ~/go/pkg.
|
||||
# skip-pkg-cache: true
|
||||
|
||||
# Optional: if set to true, then the action won't cache or restore ~/.cache/go-build.
|
||||
# skip-build-cache: true
|
||||
|
||||
# Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'.
|
||||
# install-mode: "goinstall"
|
||||
version: v2.4
|
||||
args: --timeout 5m
|
||||
|
2
.github/workflows/unit.yml
vendored
2
.github/workflows/unit.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.24.x'
|
||||
go-version: '1.25.x'
|
||||
- name: Install dependencies
|
||||
run: go mod download
|
||||
- name: Test with the Go CLI
|
||||
|
@@ -1,9 +1,7 @@
|
||||
version: '2'
|
||||
linters:
|
||||
# Enable specific linter
|
||||
# https://golangci-lint.run/usage/linters/#enabled-by-default
|
||||
enable:
|
||||
- staticcheck
|
||||
- govet
|
||||
output:
|
||||
formats:
|
||||
- format: colored-line-number
|
||||
|
@@ -121,10 +121,11 @@ func getCompressionWriter(file *os.File, algo string, concurrency int) (io.Write
|
||||
}
|
||||
}
|
||||
|
||||
func writeTarball(path string, tarWriter *tar.Writer, prefix string) error {
|
||||
func writeTarball(path string, tarWriter *tar.Writer, prefix string) (returnErr error) {
|
||||
fileInfo, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("error getting file info for %s", path))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("error getting file info for %s", path))
|
||||
return
|
||||
}
|
||||
|
||||
if fileInfo.Mode()&os.ModeSocket == os.ModeSocket {
|
||||
@@ -135,19 +136,22 @@ func writeTarball(path string, tarWriter *tar.Writer, prefix string) error {
|
||||
if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
var err error
|
||||
if link, err = os.Readlink(path); err != nil {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("error resolving symlink %s", path))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("error resolving symlink %s", path))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
header, err := tar.FileInfoHeader(fileInfo, link)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error getting file info header")
|
||||
returnErr = errwrap.Wrap(err, "error getting file info header")
|
||||
return
|
||||
}
|
||||
header.Name = strings.TrimPrefix(path, prefix)
|
||||
|
||||
err = tarWriter.WriteHeader(header)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error writing file info header")
|
||||
returnErr = errwrap.Wrap(err, "error writing file info header")
|
||||
return
|
||||
}
|
||||
|
||||
if !fileInfo.Mode().IsRegular() {
|
||||
@@ -156,13 +160,17 @@ func writeTarball(path string, tarWriter *tar.Writer, prefix string) error {
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("error opening %s", path))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("error opening %s", path))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
defer func() {
|
||||
returnErr = file.Close()
|
||||
}()
|
||||
|
||||
_, err = io.Copy(tarWriter, file)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("error copying %s to tar writer", path))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("error copying %s to tar writer", path))
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -153,13 +153,13 @@ func source(path string) (map[string]string, error) {
|
||||
currentValue, currentOk := os.LookupEnv(key)
|
||||
defer func() {
|
||||
if currentOk {
|
||||
os.Setenv(key, currentValue)
|
||||
_ = os.Setenv(key, currentValue)
|
||||
return
|
||||
}
|
||||
os.Unsetenv(key)
|
||||
_ = os.Unsetenv(key)
|
||||
}()
|
||||
result[key] = value
|
||||
os.Setenv(key, value)
|
||||
_ = os.Setenv(key, value)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
|
@@ -60,8 +60,10 @@ func TestSource(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
os.Setenv("QUX", "yyy")
|
||||
defer os.Unsetenv("QUX")
|
||||
_ = os.Setenv("QUX", "yyy")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("QUX")
|
||||
}()
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
@@ -177,8 +177,12 @@ func (s *script) runLabeledCommands(label string) error {
|
||||
s.logger.Info(fmt.Sprintf("Running %s command %s for container %s", label, cmd, strings.TrimPrefix(c.Names[0], "/")))
|
||||
stdout, stderr, err := s.exec(c.ID, cmd, user)
|
||||
if s.c.ExecForwardOutput {
|
||||
os.Stderr.Write(stderr)
|
||||
os.Stdout.Write(stdout)
|
||||
if _, err := os.Stderr.Write(stderr); err != nil {
|
||||
return errwrap.Wrap(err, "error writing to stderr")
|
||||
}
|
||||
if _, err := os.Stdout.Write(stdout); err != nil {
|
||||
return errwrap.Wrap(err, "error writing to stdout")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error executing command")
|
||||
|
@@ -13,7 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command/service/progress"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
ctr "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
@@ -66,7 +65,7 @@ func awaitContainerCountForService(cli *client.Client, serviceID string, count i
|
||||
),
|
||||
)
|
||||
case <-poll.C:
|
||||
containers, err := cli.ContainerList(context.Background(), container.ListOptions{
|
||||
containers, err := cli.ContainerList(context.Background(), ctr.ListOptions{
|
||||
Filters: filters.NewArgs(filters.KeyValuePair{
|
||||
Key: "label",
|
||||
Value: fmt.Sprintf("com.docker.swarm.service.id=%s", serviceID),
|
||||
@@ -124,11 +123,11 @@ func (s *script) stopContainersAndServices() (func() error, error) {
|
||||
labelValue,
|
||||
)
|
||||
|
||||
allContainers, err := s.cli.ContainerList(context.Background(), container.ListOptions{})
|
||||
allContainers, err := s.cli.ContainerList(context.Background(), ctr.ListOptions{})
|
||||
if err != nil {
|
||||
return noop, errwrap.Wrap(err, "error querying for containers")
|
||||
}
|
||||
containersToStop, err := s.cli.ContainerList(context.Background(), container.ListOptions{
|
||||
containersToStop, err := s.cli.ContainerList(context.Background(), ctr.ListOptions{
|
||||
Filters: filters.NewArgs(filters.KeyValuePair{
|
||||
Key: "label",
|
||||
Value: filterMatchLabel,
|
||||
@@ -215,7 +214,7 @@ func (s *script) stopContainersAndServices() (func() error, error) {
|
||||
)
|
||||
}
|
||||
|
||||
var stoppedContainers []container.Summary
|
||||
var stoppedContainers []ctr.Summary
|
||||
var stopErrors []error
|
||||
for _, container := range containersToStop {
|
||||
if err := s.cli.ContainerStop(context.Background(), container.ID, ctr.StopOptions{}); err != nil {
|
||||
|
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/offen/docker-volume-backup
|
||||
|
||||
go 1.24.0
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
filippo.io/age v1.2.1
|
||||
|
@@ -87,7 +87,7 @@ func (b *dropboxStorage) Name() string {
|
||||
}
|
||||
|
||||
// Copy copies the given file to the WebDav storage backend.
|
||||
func (b *dropboxStorage) Copy(file string) error {
|
||||
func (b *dropboxStorage) Copy(file string) (returnErr error) {
|
||||
_, name := path.Split(file)
|
||||
|
||||
folderArg := files.NewCreateFolderArg(b.DestinationPath)
|
||||
@@ -95,19 +95,24 @@ func (b *dropboxStorage) Copy(file string) error {
|
||||
switch err := err.(type) {
|
||||
case files.CreateFolderV2APIError:
|
||||
if err.EndpointError.Path.Tag != files.WriteErrorConflict {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("error creating directory '%s'", b.DestinationPath))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("error creating directory '%s'", b.DestinationPath))
|
||||
return
|
||||
}
|
||||
b.Log(storage.LogLevelInfo, b.Name(), "Destination path '%s' already exists, no new directory required.", b.DestinationPath)
|
||||
default:
|
||||
return errwrap.Wrap(err, fmt.Sprintf("error creating directory '%s'", b.DestinationPath))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("error creating directory '%s'", b.DestinationPath))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
r, err := os.Open(file)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error opening the file to be uploaded")
|
||||
returnErr = errwrap.Wrap(err, "error opening the file to be uploaded")
|
||||
return
|
||||
}
|
||||
defer r.Close()
|
||||
defer func() {
|
||||
returnErr = r.Close()
|
||||
}()
|
||||
|
||||
// Start new upload session and get session id
|
||||
b.Log(storage.LogLevelInfo, b.Name(), "Starting upload session for backup '%s' at path '%s'.", file, b.DestinationPath)
|
||||
@@ -116,7 +121,8 @@ func (b *dropboxStorage) Copy(file string) error {
|
||||
uploadSessionStartArg := files.NewUploadSessionStartArg()
|
||||
uploadSessionStartArg.SessionType = &files.UploadSessionType{Tagged: dropbox.Tagged{Tag: files.UploadSessionTypeConcurrent}}
|
||||
if res, err := b.client.UploadSessionStart(uploadSessionStartArg, nil); err != nil {
|
||||
return errwrap.Wrap(err, "error starting the upload session")
|
||||
returnErr = errwrap.Wrap(err, "error starting the upload session")
|
||||
return
|
||||
} else {
|
||||
sessionId = res.SessionId
|
||||
}
|
||||
@@ -197,7 +203,8 @@ loop:
|
||||
files.NewCommitInfo(path.Join(b.DestinationPath, name)),
|
||||
), nil)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error finishing the upload session")
|
||||
returnErr = errwrap.Wrap(err, "error finishing the upload session")
|
||||
return
|
||||
}
|
||||
|
||||
b.Log(storage.LogLevelInfo, b.Name(), "Uploaded a copy of backup '%s' at path '%s'.", file, b.DestinationPath)
|
||||
|
@@ -11,14 +11,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"crypto/tls"
|
||||
"github.com/offen/docker-volume-backup/internal/errwrap"
|
||||
"github.com/offen/docker-volume-backup/internal/storage"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/drive/v3"
|
||||
"google.golang.org/api/option"
|
||||
"golang.org/x/oauth2"
|
||||
"net/http"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
type googleDriveStorage struct {
|
||||
@@ -84,15 +84,18 @@ func (b *googleDriveStorage) Name() string {
|
||||
}
|
||||
|
||||
// Copy copies the given file to the Google Drive storage backend.
|
||||
func (b *googleDriveStorage) Copy(file string) error {
|
||||
func (b *googleDriveStorage) Copy(file string) (returnErr error) {
|
||||
_, name := filepath.Split(file)
|
||||
b.Log(storage.LogLevelInfo, b.Name(), "Starting upload for backup '%s'.", name)
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("failed to open file %s", file))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("failed to open file %s", file))
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() {
|
||||
returnErr = f.Close()
|
||||
}()
|
||||
|
||||
driveFile := &drive.File{Name: name}
|
||||
if b.DestinationPath != "" {
|
||||
@@ -104,7 +107,8 @@ func (b *googleDriveStorage) Copy(file string) error {
|
||||
createCall := b.client.Files.Create(driveFile).SupportsAllDrives(true).Fields("id")
|
||||
created, err := createCall.Media(f).Do()
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, fmt.Sprintf("failed to upload %s", name))
|
||||
returnErr = errwrap.Wrap(err, fmt.Sprintf("failed to upload %s", name))
|
||||
return
|
||||
}
|
||||
|
||||
b.Log(storage.LogLevelInfo, b.Name(), "Finished upload for %s. File ID: %s", name, created.Id)
|
||||
|
@@ -55,7 +55,9 @@ func (b *localStorage) Copy(file string) error {
|
||||
if b.latestSymlink != "" {
|
||||
symlink := path.Join(b.DestinationPath, b.latestSymlink)
|
||||
if _, err := os.Lstat(symlink); err == nil {
|
||||
os.Remove(symlink)
|
||||
if err := os.Remove(symlink); err != nil {
|
||||
return errwrap.Wrap(err, "error removing existing symlink")
|
||||
}
|
||||
}
|
||||
if err := os.Symlink(name, symlink); err != nil {
|
||||
return errwrap.Wrap(err, "error creating latest symlink")
|
||||
@@ -146,22 +148,25 @@ func (b *localStorage) Prune(deadline time.Time, pruningPrefix string) (*storage
|
||||
}
|
||||
|
||||
// copy creates a copy of the file located at `dst` at `src`.
|
||||
func copyFile(src, dst string) error {
|
||||
func copyFile(src, dst string) (returnErr error) {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
returnErr = err
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
defer func() {
|
||||
returnErr = in.Close()
|
||||
}()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
returnErr = err
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
out.Close()
|
||||
return err
|
||||
return errors.Join(err, out.Close())
|
||||
}
|
||||
return out.Close()
|
||||
}
|
||||
|
@@ -106,19 +106,25 @@ func (b *sshStorage) Name() string {
|
||||
}
|
||||
|
||||
// Copy copies the given file to the SSH storage backend.
|
||||
func (b *sshStorage) Copy(file string) error {
|
||||
func (b *sshStorage) Copy(file string) (returnErr error) {
|
||||
source, err := os.Open(file)
|
||||
_, name := path.Split(file)
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, " error reading the file to be uploaded")
|
||||
returnErr = errwrap.Wrap(err, " error reading the file to be uploaded")
|
||||
return
|
||||
}
|
||||
defer source.Close()
|
||||
defer func() {
|
||||
returnErr = source.Close()
|
||||
}()
|
||||
|
||||
destination, err := b.sftpClient.Create(path.Join(b.DestinationPath, name))
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error creating file")
|
||||
returnErr = errwrap.Wrap(err, "error creating file")
|
||||
return
|
||||
}
|
||||
defer destination.Close()
|
||||
defer func() {
|
||||
returnErr = destination.Close()
|
||||
}()
|
||||
|
||||
chunk := make([]byte, 1e9)
|
||||
for {
|
||||
@@ -126,27 +132,32 @@ func (b *sshStorage) Copy(file string) error {
|
||||
if err == io.EOF {
|
||||
tot, err := destination.Write(chunk[:num])
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error uploading the file")
|
||||
returnErr = errwrap.Wrap(err, "error uploading the file")
|
||||
return
|
||||
}
|
||||
|
||||
if tot != len(chunk[:num]) {
|
||||
return errwrap.Wrap(nil, "failed to write stream")
|
||||
returnErr = errwrap.Wrap(nil, "failed to write stream")
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error uploading the file")
|
||||
returnErr = errwrap.Wrap(err, "error uploading the file")
|
||||
return
|
||||
}
|
||||
|
||||
tot, err := destination.Write(chunk[:num])
|
||||
if err != nil {
|
||||
return errwrap.Wrap(err, "error uploading the file")
|
||||
returnErr = errwrap.Wrap(err, "error uploading the file")
|
||||
return
|
||||
}
|
||||
|
||||
if tot != len(chunk[:num]) {
|
||||
return errwrap.Wrap(nil, "failed to write stream")
|
||||
returnErr = errwrap.Wrap(nil, "failed to write stream")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user