Compare commits
3 Commits
add-file-u
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 21db79a333 | |||
| d316b8ff4b | |||
| cab35ddfdd |
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module backup-tool
|
module backup-tool
|
||||||
|
|
||||||
go 1.21
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
|||||||
49
helpers/helpers.go
Normal file
49
helpers/helpers.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetCredentials() (string, string, error) {
|
||||||
|
// get credentials from env vars
|
||||||
|
keyName := os.Getenv("KEY_ID")
|
||||||
|
if keyName == "" {
|
||||||
|
return "", "", errors.New("missing or empty KEY_ID")
|
||||||
|
}
|
||||||
|
applicationKey := os.Getenv("APPLICATION_KEY")
|
||||||
|
if applicationKey == "" {
|
||||||
|
return "", "", errors.New("missing or empty APPLICATION_KEY")
|
||||||
|
}
|
||||||
|
return keyName, applicationKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateClient(accessKeyID, secretAccessKey, endpoint string) (*minio.Client, error) {
|
||||||
|
// Initialize minio client object.
|
||||||
|
minioClient, err := minio.New(endpoint, &minio.Options{
|
||||||
|
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
|
||||||
|
Secure: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return minioClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyBucket(client *minio.Client, ctx context.Context, bucketName string) error {
|
||||||
|
exists, err := client.BucketExists(ctx, bucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return errors.New(fmt.Sprintf("bucket %s does not exist\n", bucketName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
62
main.go
62
main.go
@@ -1,44 +1,50 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"backup-tool/pruner"
|
||||||
|
"backup-tool/uploader"
|
||||||
|
"flag"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ENDPOINT = "s3.us-west-004.backblazeb2.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
var skipUpload bool
|
||||||
|
var keepNum int
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&skipUpload, "skip-upload", false, "do not upload file to S3/Backblaze")
|
||||||
|
flag.IntVar(&keepNum, "keep-backups", 7, "number of backups to keep; 0 to keep all")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
accessKeyID, secretAccessKey := getCredentials()
|
|
||||||
endpoint := "s3.us-west-002.backblazeb2.com"
|
|
||||||
|
|
||||||
// Initialize minio client object.
|
|
||||||
minioClient, err := minio.New(endpoint, &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
|
|
||||||
Secure: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("%#v\n", minioClient) // minioClient is now set up
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCredentials() (string, string) {
|
|
||||||
// get credentials from env vars
|
|
||||||
err := godotenv.Load()
|
err := godotenv.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error loading .env file: (%s)", err.Error())
|
log.Fatalf("error loading .env file: (%s)", err.Error())
|
||||||
}
|
}
|
||||||
keyName := os.Getenv("KEY_NAME")
|
|
||||||
if keyName == "" {
|
if len(os.Args) < 2 && !skipUpload {
|
||||||
log.Fatal("missing or empty KEY_NAME")
|
log.Fatalln("missing filename parameter")
|
||||||
}
|
}
|
||||||
applicationKey := os.Getenv("APPLICATION_KEY")
|
|
||||||
if applicationKey == "" {
|
bucketName := os.Getenv("BUCKET_NAME")
|
||||||
log.Fatal("missing or empty APPLICATION_KEY")
|
if bucketName == "" {
|
||||||
|
log.Fatalln("missing or empty BUCKET_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !skipUpload {
|
||||||
|
err = uploader.UploadFile(bucketName, ENDPOINT, os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if keepNum > 0 {
|
||||||
|
err = pruner.PruneFiles(bucketName, ENDPOINT, keepNum)
|
||||||
}
|
}
|
||||||
return keyName, applicationKey
|
|
||||||
}
|
}
|
||||||
|
|||||||
52
pruner/pruner.go
Normal file
52
pruner/pruner.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package pruner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"backup-tool/helpers"
|
||||||
|
"context"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PruneFiles(bucket, endpoint string, keep int) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
accessKeyID, secretAccessKey, err := helpers.GetCredentials()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client, err := helpers.CreateClient(accessKeyID, secretAccessKey, endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = helpers.VerifyBucket(client, ctx, bucket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var objects []minio.ObjectInfo
|
||||||
|
for obj := range client.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) {
|
||||||
|
objects = append(objects, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(objects) < keep {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(BucketObjects(objects)) // last modified object will be at position 0
|
||||||
|
for _, object := range objects[keep:] {
|
||||||
|
err := client.RemoveObject(ctx, bucket, object.Key, minio.RemoveObjectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error deleting object %q: %s", object.Key, err.Error())
|
||||||
|
} else {
|
||||||
|
log.Printf("deleted object %q", object.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type BucketObjects []minio.ObjectInfo
|
||||||
|
|
||||||
|
func (b BucketObjects) Len() int { return len(b) }
|
||||||
|
func (b BucketObjects) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
func (b BucketObjects) Less(i, j int) bool { return b[i].LastModified.After(b[j].LastModified) } // return newest object first in list
|
||||||
52
uploader/uploader.go
Normal file
52
uploader/uploader.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package uploader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"backup-tool/helpers"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UploadFile(bucketName, endpoint, fileName string) error {
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
accessKeyID, secretAccessKey, err := helpers.GetCredentials()
|
||||||
|
if err != nil {
|
||||||
|
return (err)
|
||||||
|
}
|
||||||
|
client, err := helpers.CreateClient(accessKeyID, secretAccessKey, endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return (err)
|
||||||
|
}
|
||||||
|
err = helpers.VerifyBucket(client, ctx, bucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
absolutePath, basename, err := validateUploadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.FPutObject(ctx, bucketName, basename, absolutePath, minio.PutObjectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("upload of %s complete\n", basename)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateUploadFile(fileName string) (string, string, error) {
|
||||||
|
file, err := os.Stat(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.IsDir() {
|
||||||
|
return "", "", errors.New("upload of directories is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileName, file.Name(), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user