-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.go
126 lines (104 loc) · 3.2 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package main
import (
"context"
"fmt"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/joho/godotenv"
"log"
"os"
"path/filepath"
"reflect"
"strconv"
"time"
)
var client *hcloud.Client
var keepAmount int
func main() {
// Get executable dir
ex, err := os.Executable()
if err != nil {
log.Fatalf("error getting executable path: %s", err)
}
exPath, err := filepath.EvalSymlinks(filepath.Dir(ex))
if err != nil {
panic(err)
}
log.Printf("Looking for .env file in %s", exPath)
// Load config and initialize
tryLoadDotEnv(filepath.Join(exPath, ".env"), ".env")
keepAmount, err = strconv.Atoi(os.Getenv("KEEP_AMOUNT"))
if err != nil {
log.Fatalf("environment variable KEEP_AMOUNT can't be converted to an integer: %s", err)
}
client = hcloud.NewClient(hcloud.WithToken(os.Getenv("HCLOUD_TOKEN")))
// Loop servers and run backup for each
servers, err := client.Server.All(context.Background())
if err != nil {
log.Fatalf("error retrieving servers: %s\n", err)
}
for _, server := range servers {
if server.BackupWindow != "" {
log.Printf("%s has backups enabled, skipping\n", server.Name)
continue
}
// TODO: create backups async
createBackup(server)
pruneBackups(server)
}
}
//tryLoadDotEnv load dot env files but don't abort when one doesn't exist
func tryLoadDotEnv(filenames ...string) {
for _, filename := range filenames {
err := godotenv.Load(filename)
if err != nil && !os.IsNotExist(err) {
log.Fatalf("error loading .env file: %s", reflect.TypeOf(err))
}
}
}
func createBackup(server *hcloud.Server) {
log.Printf("creating backup for %s\n", server.Name)
image, _, err := client.Server.CreateImage(context.Background(), server, &hcloud.ServerCreateImageOpts{
Type: hcloud.ImageTypeSnapshot,
Description: hcloud.String(fmt.Sprintf("Backup %s %s", server.Name, time.Now().Format("Jan 02 2006 15:04 MST"))),
Labels: map[string]string{"autobackup": ""},
})
if err != nil {
log.Fatalf("failed to create backup image: %s\n", err)
}
waitForAction(image.Action)
log.Printf("successfully created image: %s\n", image.Image.Description)
}
func pruneBackups(server *hcloud.Server) {
// Get all images with `autobackup` label, sorted by creation date (most recent first)
images, err := client.Image.AllWithOpts(context.Background(), hcloud.ImageListOpts{
ListOpts: hcloud.ListOpts{
LabelSelector: "autobackup",
},
Sort: []string{"created:desc"},
Type: []hcloud.ImageType{hcloud.ImageTypeSnapshot},
})
if err != nil {
log.Fatalf("error retrieving images: %s\n", err)
}
backupCount := 0
for _, image := range images {
// Filter backups for current server only
if image.CreatedFrom.ID == server.ID {
backupCount++
if backupCount > keepAmount {
log.Printf("deleting backup image: %s", image.Description)
_, err := client.Image.Delete(context.Background(), image)
if err != nil {
log.Fatalf("error deleting backup image (%s): %s", image.Description, err)
}
}
}
}
}
func waitForAction(action *hcloud.Action) {
_, errors := client.Action.WatchProgress(context.Background(), action)
err := <-errors // This blocks until finished/errored
if err != nil {
log.Fatalf("action %s failed: %s\n", action.Command, err)
}
}