Skip to content

Commit

Permalink
handle remote & local in any order
Browse files Browse the repository at this point in the history
  • Loading branch information
tomcz committed Nov 7, 2022
1 parent 88c175b commit 3e52a6d
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 24 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ NAME:
s3backup put - Upload file to S3 bucket using local credentials
USAGE:
s3backup put [command options] s3://bucket/objectkey local_file_path
s3backup put [command options] local_file_path s3://bucket/objectkey
OPTIONS:
--symKey value Password to use for symmetric AES encryption (optional)
Expand Down Expand Up @@ -102,7 +102,7 @@ NAME:
s3backup vault-put - Upload file to S3 bucket using credentials from vault
USAGE:
s3backup vault-put [command options] s3://bucket/objectkey local_file_path
s3backup vault-put [command options] local_file_path s3://bucket/objectkey
OPTIONS:
--role value Vault role_id to retrieve backup credentials (either role & secret, or token)
Expand Down
23 changes: 18 additions & 5 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package client

import (
"errors"
"log"
"os"
)
Expand All @@ -14,17 +15,23 @@ type Client struct {
}

func (c *Client) GetRemoteFile(remotePath, localPath string) error {
tempFile := localPath
if c.Store.IsRemote(remotePath) && c.Store.IsRemote(localPath) {
return errors.New("cannot have two remote paths")
}
if c.Store.IsRemote(localPath) {
localPath, remotePath = remotePath, localPath
}

tempFile := localPath
if c.Cipher != nil {
tempFile += tempFileSuffix
defer remove(tempFile)
}

log.Println("Downloading", remotePath, "to", tempFile)
checksum, err := c.Store.DownloadFile(remotePath, tempFile, c.Hash != nil)
if err != nil {
return err
checksum, cerr := c.Store.DownloadFile(remotePath, tempFile, c.Hash != nil)
if cerr != nil {
return cerr
}

if c.Hash != nil {
Expand All @@ -45,8 +52,14 @@ func (c *Client) GetRemoteFile(remotePath, localPath string) error {
}

func (c *Client) PutLocalFile(remotePath, localPath string) error {
tempFile := localPath
if c.Store.IsRemote(remotePath) && c.Store.IsRemote(localPath) {
return errors.New("cannot have two remote paths")
}
if c.Store.IsRemote(localPath) {
localPath, remotePath = remotePath, localPath
}

tempFile := localPath
if c.Cipher != nil {
tempFile += tempFileSuffix
defer remove(tempFile)
Expand Down
12 changes: 10 additions & 2 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ func TestGetRemoteFileWithoutDecryption(t *testing.T) {
Store: store,
}

store.EXPECT().IsRemote("bar.txt").Return(false).AnyTimes()
store.EXPECT().IsRemote("s3://foo/bar.txt").Return(true).AnyTimes()
store.EXPECT().DownloadFile("s3://foo/bar.txt", "bar.txt", true).Return("muahahaha", nil)
hash.EXPECT().Verify("bar.txt", "muahahaha").Return(nil)

assert.NoError(t, c.GetRemoteFile("s3://foo/bar.txt", "bar.txt"))
assert.NoError(t, c.GetRemoteFile("bar.txt", "s3://foo/bar.txt"))
}

func TestGetRemoteFileWithDecryption(t *testing.T) {
Expand All @@ -41,6 +43,8 @@ func TestGetRemoteFileWithDecryption(t *testing.T) {
Cipher: cipher,
}

store.EXPECT().IsRemote("bar.txt").Return(false).AnyTimes()
store.EXPECT().IsRemote("s3://foo/bar.txt").Return(true).AnyTimes()
store.EXPECT().DownloadFile("s3://foo/bar.txt", "bar.txt.tmp", true).Return("muahahaha", nil)
hash.EXPECT().Verify("bar.txt.tmp", "muahahaha").Return(nil)
cipher.EXPECT().Decrypt("bar.txt.tmp", "bar.txt").Return(nil)
Expand All @@ -60,10 +64,12 @@ func TestPutLocalFileWithoutEncryption(t *testing.T) {
Store: store,
}

store.EXPECT().IsRemote("bar.txt").Return(false).AnyTimes()
store.EXPECT().IsRemote("s3://foo/bar.txt").Return(true).AnyTimes()
hash.EXPECT().Calculate("bar.txt").Return("woahahaha", nil)
store.EXPECT().UploadFile("s3://foo/bar.txt", "bar.txt", "woahahaha").Return(nil)

assert.NoError(t, c.PutLocalFile("s3://foo/bar.txt", "bar.txt"))
assert.NoError(t, c.PutLocalFile("bar.txt", "s3://foo/bar.txt"))
}

func TestPutLocalFileWithEncryption(t *testing.T) {
Expand All @@ -80,6 +86,8 @@ func TestPutLocalFileWithEncryption(t *testing.T) {
Cipher: cipher,
}

store.EXPECT().IsRemote("bar.txt").Return(false).AnyTimes()
store.EXPECT().IsRemote("s3://foo/bar.txt").Return(true).AnyTimes()
cipher.EXPECT().Encrypt("bar.txt", "bar.txt.tmp").Return(nil)
hash.EXPECT().Calculate("bar.txt.tmp").Return("woahahaha", nil)
store.EXPECT().UploadFile("s3://foo/bar.txt", "bar.txt.tmp", "woahahaha").Return(nil)
Expand Down
3 changes: 1 addition & 2 deletions client/crypto/aes_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package crypto

import (
"io/ioutil"
"os"
"testing"

Expand Down Expand Up @@ -41,7 +40,7 @@ func testRoundTrip(t *testing.T, key string) {
require.NoError(t, cipher.Encrypt(file, encryptedFile), "Cannot encrypt file")
require.NoError(t, cipher.Decrypt(encryptedFile, decryptedFile), "Cannot decrypt file")

actual, err := ioutil.ReadFile(decryptedFile)
actual, err := os.ReadFile(decryptedFile)
require.NoError(t, err, "Cannot read decrypted file")

assert.Equal(t, expected, actual, "File contents are different")
Expand Down
3 changes: 1 addition & 2 deletions client/crypto/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"os"

"github.com/tomcz/s3backup/client"
Expand All @@ -31,7 +30,7 @@ type rsaCipher struct {
}

func NewRSACipher(pemKeyFile string) (client.Cipher, error) {
buf, err := ioutil.ReadFile(pemKeyFile)
buf, err := os.ReadFile(pemKeyFile)
if err != nil {
return nil, err
}
Expand Down
3 changes: 1 addition & 2 deletions client/crypto/rsa_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package crypto

import (
"io/ioutil"
"os"
"testing"

Expand Down Expand Up @@ -44,7 +43,7 @@ func TestRoundTripRSAEncryptDecrypt(t *testing.T) {
require.NoError(t, pubCipher.Encrypt(file, encryptedFile), "Cannot encrypt file")
require.NoError(t, privCipher.Decrypt(encryptedFile, decryptedFile), "Cannot decrypt file")

actual, err := ioutil.ReadFile(decryptedFile)
actual, err := os.ReadFile(decryptedFile)
require.NoError(t, err, "Cannot read decrypted file")

assert.Equal(t, expected, actual, "File contents are different")
Expand Down
14 changes: 14 additions & 0 deletions client/mocks/store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions client/store.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package client

//go:generate mockgen --source=store.go --destination=mocks/store.go --package=mocks

type Store interface {
IsRemote(path string) bool
UploadFile(remotePath, localPath, checksum string) error
DownloadFile(remotePath, localPath string, readChecksum bool) (checksum string, err error)
}

//go:generate mockgen --source=store.go --destination=mocks/store.go --package=mocks
4 changes: 4 additions & 0 deletions client/store/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func NewS3(awsAccessKey, awsSecretKey, awsToken, awsRegion, awsEndpoint string)
return &s3store{s3.New(awsSession)}, nil
}

func (s *s3store) IsRemote(path string) bool {
return s3PathPattern.MatchString(path)
}

func (s *s3store) UploadFile(remotePath, localPath, checksum string) error {
bucket, objectKey, err := splitRemotePath(remotePath)
if err != nil {
Expand Down
11 changes: 8 additions & 3 deletions client/store/s3_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package store

import (
"io/ioutil"
"net/http/httptest"
"os"
"testing"
Expand Down Expand Up @@ -33,6 +32,12 @@ func TestSplitRemotePath(t *testing.T) {
assert.Error(t, err)
}

func TestIsRemote(t *testing.T) {
store := &s3store{}
assert.True(t, store.IsRemote("s3://bucket/object.key"))
assert.False(t, store.IsRemote("wibble.txt"))
}

func TestRoundTripUploadDownload_withChecksum(t *testing.T) {
backend := s3mem.New()
faker := gofakes3.New(backend)
Expand Down Expand Up @@ -63,7 +68,7 @@ func TestRoundTripUploadDownload_withChecksum(t *testing.T) {
require.NoError(t, err, "failed to download file")
defer os.Remove(downloadFile)

actual, err := ioutil.ReadFile(downloadFile)
actual, err := os.ReadFile(downloadFile)
require.NoError(t, err, "Cannot read downloaded file")

assert.Equal(t, "wibble", checksum)
Expand Down Expand Up @@ -100,7 +105,7 @@ func TestRoundTripUploadDownload_withoutChecksum(t *testing.T) {
require.NoError(t, err, "failed to download file")
defer os.Remove(downloadFile)

actual, err := ioutil.ReadFile(downloadFile)
actual, err := os.ReadFile(downloadFile)
require.NoError(t, err, "Cannot read downloaded file")

assert.Equal(t, "", checksum)
Expand Down
4 changes: 2 additions & 2 deletions cmd/s3backup/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func main() {
cmdBasicPut := &cli.Command{
Name: "put",
Usage: "Upload file to S3 bucket using local credentials",
ArgsUsage: "s3://bucket/objectkey local_file_path",
ArgsUsage: "local_file_path s3://bucket/objectkey",
Action: basicPut,
Flags: basicFlags(true),
}
Expand All @@ -56,7 +56,7 @@ func main() {
cmdVaultPut := &cli.Command{
Name: "vault-put",
Usage: "Upload file to S3 bucket using credentials from vault",
ArgsUsage: "s3://bucket/objectkey local_file_path",
ArgsUsage: "local_file_path s3://bucket/objectkey",
Action: vaultPut,
Flags: vaultFlags(true),
}
Expand Down
4 changes: 2 additions & 2 deletions utils/tempfile.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package utils

import "io/ioutil"
import "os"

func CreateTempFile(prefix string, body []byte) (string, error) {
file, err := ioutil.TempFile("", prefix)
file, err := os.CreateTemp("", prefix)
if err != nil {
return "", err
}
Expand Down

0 comments on commit 3e52a6d

Please sign in to comment.