2021-02-04 00:25:10 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/digitalocean/godo"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2021-02-04 03:02:50 +01:00
|
|
|
const createdAtFormat = "2006-01-02T15:04:05Z"
|
2021-02-04 00:25:10 +01:00
|
|
|
|
|
|
|
type snapshotterContext struct {
|
2021-12-08 15:36:38 +01:00
|
|
|
DoContext *DigitalOceanContext
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func initLogging() {
|
|
|
|
log.SetFormatter(&log.TextFormatter{
|
|
|
|
DisableLevelTruncation: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
log.SetOutput(os.Stdout)
|
|
|
|
|
2021-02-04 04:10:34 +01:00
|
|
|
log.SetLevel(log.InfoLevel)
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
initLogging()
|
|
|
|
|
|
|
|
DOToken, present := os.LookupEnv("DO_TOKEN")
|
|
|
|
|
2021-12-08 15:36:38 +01:00
|
|
|
if !present {
|
2021-02-04 00:25:10 +01:00
|
|
|
log.Fatal("Missing enviroment variable \"DO_TOKEN\"")
|
|
|
|
}
|
|
|
|
|
|
|
|
volumesEnv, present := os.LookupEnv("DO_VOLUMES")
|
|
|
|
|
2021-12-08 15:36:38 +01:00
|
|
|
if !present {
|
2021-02-04 00:25:10 +01:00
|
|
|
log.Fatal("Missing enviroment variable \"DO_VOLUMES\"")
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshotCountEnv, present := os.LookupEnv("DO_SNAPSHOT_COUNT")
|
|
|
|
|
2021-12-08 15:36:38 +01:00
|
|
|
if !present {
|
2021-02-04 00:25:10 +01:00
|
|
|
log.Fatal("Missing enviroment variable \"DO_SNAPSHOT_COUNT\"")
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshotCount, err := strconv.Atoi(snapshotCountEnv)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Enviroment variable \"DO_SNAPSHOT_COUNT\" is not an integer")
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := snapshotterContext{
|
|
|
|
DoContext: &DigitalOceanContext{
|
|
|
|
client: godo.NewFromToken(DOToken),
|
|
|
|
ctx: context.TODO(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
volumeIDs := strings.Split(volumesEnv, ",")
|
|
|
|
|
|
|
|
for _, volumeID := range volumeIDs {
|
|
|
|
volume, _, err := ctx.DoContext.GetVolume(volumeID)
|
|
|
|
if err != nil {
|
2021-02-04 03:00:13 +01:00
|
|
|
handleError(ctx, err, true)
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
2021-02-04 04:13:01 +01:00
|
|
|
snapshot, _, err := ctx.DoContext.CreateSnapshot(&godo.SnapshotCreateRequest{
|
2021-02-04 00:25:10 +01:00
|
|
|
VolumeID: volume.ID,
|
2021-02-04 03:02:28 +01:00
|
|
|
Name: time.Now().Format("2006-01-02T15:04:05"),
|
2021-02-04 00:25:10 +01:00
|
|
|
})
|
2021-12-08 15:36:38 +01:00
|
|
|
|
2021-02-04 00:25:10 +01:00
|
|
|
if err != nil {
|
2021-02-04 03:00:13 +01:00
|
|
|
handleError(ctx, err, true)
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
2021-02-04 04:13:01 +01:00
|
|
|
log.Info(fmt.Sprintf("Created Snapshot with Id %s from volume %s", snapshot.ID, volume.Name))
|
|
|
|
|
2021-02-04 00:25:10 +01:00
|
|
|
snapshots, _, err := ctx.DoContext.ListSnapshots(volume.ID, nil)
|
|
|
|
|
2021-12-08 15:36:38 +01:00
|
|
|
if err != nil {
|
|
|
|
handleError(ctx, err, true)
|
|
|
|
}
|
|
|
|
|
2021-02-04 00:25:10 +01:00
|
|
|
snapshotLength := len(snapshots)
|
|
|
|
|
|
|
|
if snapshotLength > snapshotCount {
|
|
|
|
sort.SliceStable(snapshots, func(firstIndex, secondIndex int) bool {
|
2021-02-04 03:51:47 +01:00
|
|
|
firstTime, err := time.Parse(createdAtFormat, snapshots[firstIndex].Created)
|
2021-02-04 00:25:10 +01:00
|
|
|
if err != nil {
|
2021-02-04 03:00:13 +01:00
|
|
|
handleError(ctx, err, true)
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
2021-02-05 02:46:39 +01:00
|
|
|
secondTime, err := time.Parse(createdAtFormat, snapshots[secondIndex].Created)
|
2021-02-04 00:25:10 +01:00
|
|
|
if err != nil {
|
2021-02-04 03:00:13 +01:00
|
|
|
handleError(ctx, err, true)
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return firstTime.Before(secondTime)
|
|
|
|
})
|
|
|
|
|
|
|
|
snapshotsToDelete := snapshotLength - snapshotCount
|
|
|
|
|
|
|
|
for i := 0; i < snapshotsToDelete; i++ {
|
2021-02-04 04:13:01 +01:00
|
|
|
snapshotToDeleteID := snapshots[i].ID
|
|
|
|
_, err := ctx.DoContext.DeleteSnapshot(snapshotToDeleteID)
|
2021-02-04 00:25:10 +01:00
|
|
|
if err != nil {
|
2021-02-04 03:00:13 +01:00
|
|
|
handleError(ctx, err, false)
|
2021-02-04 00:25:10 +01:00
|
|
|
return
|
|
|
|
}
|
2021-02-04 04:13:01 +01:00
|
|
|
|
|
|
|
log.Info(fmt.Sprintf("Deleted Snapshot with Id %s", snapshotToDeleteID))
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-04 04:13:22 +01:00
|
|
|
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|
|
|
|
|
2021-02-04 03:00:13 +01:00
|
|
|
func handleError(ctx snapshotterContext, err error, fatal bool) {
|
2021-02-04 02:45:11 +01:00
|
|
|
errString := err.Error()
|
2021-02-04 00:25:10 +01:00
|
|
|
|
2021-02-04 02:45:11 +01:00
|
|
|
if fatal {
|
|
|
|
log.Fatal(errString)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Error(errString)
|
2021-02-04 00:25:10 +01:00
|
|
|
}
|