Repository navigation
Expand file tree
/
Copy pathbackup.go
More file actions
150 lines (117 loc) · 3.42 KB
/
Copy pathbackup.go
File metadata and controls
150 lines (117 loc) · 3.42 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package cmd
import (
"context"
"fmt"
"net"
"os"
"path"
"strconv"
"time"
"github.com/go-co-op/gocron"
"github.com/james4k/rcon"
"github.com/spf13/cobra"
"go.uber.org/zap"
"github.com/saulmaldonado/agones-mc/internal/config"
"github.com/saulmaldonado/agones-mc/pkg/backup"
"github.com/saulmaldonado/agones-mc/pkg/backup/google"
"github.com/saulmaldonado/agones-mc/pkg/signal"
)
var backupCmd = cobra.Command{
Use: "backup",
Short: "Saves and backsup minecraft world",
Long: "backup is for saving and backup up current minecraft world",
Run: func(cmd *cobra.Command, args []string) {
cfg := config.NewBackupConfig()
dur := cfg.GetInitialDelay()
if dur > 0 {
logger.Info("initial delay...", zap.Duration("duration", dur))
time.Sleep(dur)
}
if cron := cfg.GetBackupCron(); cron != "" {
stop := signal.SetupSignalHandler(logger)
s := gocron.NewScheduler(time.UTC)
s.Cron(cron).Do(func() {
if err := RunBackup(cfg); err != nil {
logger.Error("backup failed", zap.String("serverName", cfg.GetPodName()), zap.Error(err))
} else {
logger.Info("backup successful", zap.String("serverName", cfg.GetPodName()))
}
})
s.StartAsync()
<-stop // SIGTERM
s.Clear()
s.Stop()
// attempt a final backup before terminating
}
if err := RunBackup(cfg); err != nil {
logger.Fatal("backup failed", zap.String("serverName", cfg.GetPodName()))
}
logger.Info("backup successful", zap.String("serverName", cfg.GetPodName()))
},
}
func init() {
RootCmd.AddCommand(&backupCmd)
}
func RunBackup(cfg config.BackupConfig) error {
// Run save-all on minecraft server to force save-all before backup
if err := saveAll(cfg.GetHost(), cfg.GetPort(), cfg.GetRCONPassword()); err != nil {
logger.Warn("error saving world. skipping save-all", zap.Error(err))
}
// Authenticate and create Google Cloud Storage client
cloudStorageClient, err := google.New(context.Background(), cfg.GetBucketName())
if err != nil {
logger.Error("error connecting to bucket", zap.Error(err))
return err
}
defer cloudStorageClient.Close()
backupName := fmt.Sprintf("%s-%v.zip", cfg.GetPodName(), time.Now().Format(time.RFC3339))
var worldPath string
if cfg.GetEdition() == config.JavaEdition {
worldPath = path.Join(cfg.GetVolume(), "worlds", "Bedrock level")
} else {
worldPath = path.Join(cfg.GetVolume(), "world")
}
// Create zip backup
err = backup.Zipit(worldPath, backupName)
if err != nil {
logger.Error("error creating zip backup", zap.Error(err))
return err
}
file, err := os.Open(backupName)
if err != nil {
logger.Error("error creating zip backup", zap.Error(err))
return err
}
defer file.Close()
// Backup to Google Cloud Storage
if err := cloudStorageClient.Backup(file); err != nil {
logger.Error("error backup up to bucket", zap.Error(err))
return err
}
os.Remove(backupName)
return nil
}
func saveAll(host string, port int, password string) error {
if password == "" {
return fmt.Errorf("password env var is empty")
}
hostport := net.JoinHostPort(host, strconv.Itoa(port))
rc, err := rcon.Dial(hostport, password)
if err != nil {
return err
}
defer rc.Close()
reqId, err := rc.Write("save-all")
if err != nil {
return err
}
res, resId, err := rc.Read()
if err != nil {
return err
}
if reqId != resId {
logger.Warn("mismatch RCON request and response id", zap.Int("reqId", reqId), zap.Int("resId", resId))
}
logger.Info(res)
return nil
}