Skip to content

Commit 28612ab

Browse files
authored
Merge pull request #1203 from percona/PBM-1618-minio-storage-type
PBM-1618: Minio as storage type for Custom-S3 storages
2 parents 8a28acb + dd3d3ff commit 28612ab

39 files changed

+5929
-14
lines changed

cmd/pbm/status.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,7 @@ func getStorageStat(
540540
}
541541

542542
s.Type = cfg.Storage.Typ()
543-
544-
if cfg.Storage.Type == storage.S3 {
545-
s.Region = cfg.Storage.S3.Region
546-
}
543+
s.Region = cfg.Storage.Region()
547544
s.Path = cfg.Storage.Path()
548545

549546
bcps, err := pbm.GetAllBackups(ctx)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/docker/go-connections v0.6.0
2020
github.com/fsnotify/fsnotify v1.9.0
2121
github.com/golang/snappy v1.0.0
22+
github.com/google/go-cmp v0.7.0
2223
github.com/google/uuid v1.6.0
2324
github.com/googleapis/gax-go/v2 v2.15.0
2425
github.com/klauspost/compress v1.18.0

packaging/conf/pbm-conf-reference.yml

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010

1111
#storage:
1212

13-
## Remote backup storage type. Supported types: S3, GCS, shared filesystem, Azure
13+
## Remote backup storage type. Supported types:
14+
## - S3,
15+
## - GCS,
16+
## - Minio (for S3 compatible storage),
17+
## - shared filesystem,
18+
## - Azure
1419

1520

1621
#---------------------S3 Storage Configuration--------------------------
@@ -69,6 +74,53 @@
6974
#
7075
## The maximum object size that will be stored on the storage
7176
# maxObjSizeGB: 5018
77+
#
78+
79+
#---------------------Minio Storage Configuration--------------------------
80+
# type:
81+
# minio:
82+
83+
## Specify the location and name of the bucket that you have configured on the storage
84+
# region:
85+
# bucket:
86+
87+
## The data directory to store backups in.
88+
## When undefined, backups are saved at the root of the bucket.
89+
# prefix:
90+
91+
## An optional custom URL to access the bucket. Useful for S3-compatible storage (e.g. MinIO)
92+
# endpointUrl:
93+
94+
#
95+
## Use https
96+
# secure: true
97+
98+
## Allow PBM to upload data to storage with self-issued TLS certificates.
99+
## Use it with caution as it might leave a hole for man-in-the-middle attacks.
100+
# insecureSkipTLSVerify: false
101+
102+
## Enable path-style URL, by default it uses virtual-hosted style
103+
# forcePathStyle: false
104+
105+
## Access credentials
106+
# credentials:
107+
# access-key-id:
108+
# secret-access-key:
109+
# session-token:
110+
# signature-ver: V4
111+
112+
## The size of data chunks in bytes to be uploaded to the storage bucket in a single request.
113+
# partSize: 10485760
114+
115+
## Enable debug trace of HTTP communication
116+
# debugTrace: true
117+
118+
## Retry upload configuration options.
119+
# retryer:
120+
# numMaxRetries: 3
121+
#
122+
## The maximum object size that will be stored on the storage
123+
# maxObjSizeGB: 5018
72124

73125
#--------------------Google Cloud Storage Configuration-----------------
74126

pbm/config/config.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/percona/percona-backup-mongodb/pbm/storage/azure"
2727
"github.com/percona/percona-backup-mongodb/pbm/storage/fs"
2828
"github.com/percona/percona-backup-mongodb/pbm/storage/gcs"
29+
"github.com/percona/percona-backup-mongodb/pbm/storage/mio"
2930
"github.com/percona/percona-backup-mongodb/pbm/storage/oss"
3031
"github.com/percona/percona-backup-mongodb/pbm/storage/s3"
3132
"github.com/percona/percona-backup-mongodb/pbm/topo"
@@ -142,6 +143,17 @@ func (c *Config) String() string {
142143
c.Storage.S3.ServerSideEncryption.SseCustomerKey = "***"
143144
}
144145
}
146+
if c.Storage.Minio != nil {
147+
if c.Storage.Minio.Credentials.AccessKeyID != "" {
148+
c.Storage.Minio.Credentials.AccessKeyID = "***"
149+
}
150+
if c.Storage.Minio.Credentials.SecretAccessKey != "" {
151+
c.Storage.Minio.Credentials.SecretAccessKey = "***"
152+
}
153+
if c.Storage.Minio.Credentials.SessionToken != "" {
154+
c.Storage.Minio.Credentials.SessionToken = "***"
155+
}
156+
}
145157
if c.Storage.Azure != nil {
146158
if c.Storage.Azure.Credentials.Key != "" {
147159
c.Storage.Azure.Credentials.Key = "***"
@@ -236,6 +248,7 @@ func (cfg *PITRConf) Clone() *PITRConf {
236248
type StorageConf struct {
237249
Type storage.Type `bson:"type" json:"type" yaml:"type"`
238250
S3 *s3.Config `bson:"s3,omitempty" json:"s3,omitempty" yaml:"s3,omitempty"`
251+
Minio *mio.Config `bson:"minio,omitempty" json:"minio,omitempty" yaml:"minio,omitempty"`
239252
GCS *gcs.Config `bson:"gcs,omitempty" json:"gcs,omitempty" yaml:"gcs,omitempty"`
240253
Azure *azure.Config `bson:"azure,omitempty" json:"azure,omitempty" yaml:"azure,omitempty"`
241254
Filesystem *fs.Config `bson:"filesystem,omitempty" json:"filesystem,omitempty" yaml:"filesystem,omitempty"`
@@ -256,6 +269,8 @@ func (s *StorageConf) Clone() *StorageConf {
256269
rv.Filesystem = s.Filesystem.Clone()
257270
case storage.S3:
258271
rv.S3 = s.S3.Clone()
272+
case storage.Minio:
273+
rv.Minio = s.Minio.Clone()
259274
case storage.Azure:
260275
rv.Azure = s.Azure.Clone()
261276
case storage.GCS:
@@ -276,6 +291,8 @@ func (s *StorageConf) Equal(other *StorageConf) bool {
276291
switch s.Type {
277292
case storage.S3:
278293
return s.S3.Equal(other.S3)
294+
case storage.Minio:
295+
return s.Minio.Equal(other.Minio)
279296
case storage.Azure:
280297
return s.Azure.Equal(other.Azure)
281298
case storage.GCS:
@@ -301,6 +318,8 @@ func (s *StorageConf) IsSameStorage(other *StorageConf) bool {
301318
switch s.Type {
302319
case storage.S3:
303320
return s.S3.IsSameStorage(other.S3)
321+
case storage.Minio:
322+
return s.Minio.IsSameStorage(other.Minio)
304323
case storage.Azure:
305324
return s.Azure.IsSameStorage(other.Azure)
306325
case storage.GCS:
@@ -320,6 +339,8 @@ func (s *StorageConf) Cast() error {
320339
return s.Filesystem.Cast()
321340
case storage.S3:
322341
return s.S3.Cast()
342+
case storage.Minio:
343+
return s.Minio.Cast()
323344
case storage.OSS:
324345
return s.OSS.Cast()
325346
case storage.GCS:
@@ -337,6 +358,8 @@ func (s *StorageConf) Typ() string {
337358
switch s.Type {
338359
case storage.S3:
339360
return "S3"
361+
case storage.Minio:
362+
return "Minio"
340363
case storage.Azure:
341364
return "Azure"
342365
case storage.GCS:
@@ -368,6 +391,19 @@ func (s *StorageConf) Path() string {
368391
if s.S3.Prefix != "" {
369392
path += "/" + s.S3.Prefix
370393
}
394+
case storage.Minio:
395+
path = s.Minio.Endpoint
396+
if path == "" {
397+
path = "minio://" + s.Minio.Bucket
398+
} else {
399+
if !strings.Contains(path, "://") {
400+
path = "minio://" + path
401+
}
402+
path += "/" + s.Minio.Bucket
403+
}
404+
if s.Minio.Prefix != "" {
405+
path += "/" + s.Minio.Prefix
406+
}
371407
case storage.Azure:
372408
epURL := s.Azure.EndpointURL
373409
if epURL == "" {
@@ -389,6 +425,19 @@ func (s *StorageConf) Path() string {
389425
return path
390426
}
391427

428+
func (s *StorageConf) Region() string {
429+
region := ""
430+
431+
switch s.Type {
432+
case storage.S3:
433+
region = s.S3.Region
434+
case storage.Minio:
435+
region = s.Minio.Region
436+
}
437+
438+
return region
439+
}
440+
392441
// RestoreConf is config options for the restore
393442
//
394443
//nolint:lll

pbm/config/config_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package config
33
import (
44
"testing"
55

6+
"github.com/google/go-cmp/cmp"
7+
68
"github.com/percona/percona-backup-mongodb/pbm/storage/azure"
79
"github.com/percona/percona-backup-mongodb/pbm/storage/fs"
810
"github.com/percona/percona-backup-mongodb/pbm/storage/gcs"
11+
"github.com/percona/percona-backup-mongodb/pbm/storage/mio"
912
"github.com/percona/percona-backup-mongodb/pbm/storage/s3"
1013
)
1114

@@ -151,6 +154,57 @@ func TestIsSameStorage(t *testing.T) {
151154
t.Errorf("storage instances has different bucket: cfg=%+v, eq=%+v", cfg, neq)
152155
}
153156
})
157+
158+
t.Run("minio", func(t *testing.T) {
159+
cfg := &mio.Config{
160+
Region: "eu",
161+
Endpoint: "ep.com",
162+
Bucket: "b1",
163+
Prefix: "p1",
164+
Credentials: mio.Credentials{
165+
AccessKeyID: "k1",
166+
SecretAccessKey: "k2",
167+
SessionToken: "sess",
168+
},
169+
Secure: true,
170+
PartSize: 6 << 20,
171+
Retryer: &mio.Retryer{},
172+
}
173+
eq := &mio.Config{
174+
Region: "eu",
175+
Endpoint: "ep.com",
176+
Bucket: "b1",
177+
Prefix: "p1",
178+
}
179+
if !cfg.IsSameStorage(eq) {
180+
t.Errorf("config storage should identify the same instance: cfg=%+v, eq=%+v, diff=%s",
181+
cfg, eq, cmp.Diff(*cfg, *eq))
182+
}
183+
184+
neq := cfg.Clone()
185+
neq.Region = "us"
186+
if cfg.IsSameStorage(neq) {
187+
t.Errorf("storage instances has different region: cfg=%+v, eq=%+v", cfg, neq)
188+
}
189+
190+
neq = cfg.Clone()
191+
neq.Endpoint = "ep2.com"
192+
if cfg.IsSameStorage(neq) {
193+
t.Errorf("storage instances has different EndpointURL: cfg=%+v, eq=%+v", cfg, neq)
194+
}
195+
196+
neq = cfg.Clone()
197+
neq.Bucket = "b2"
198+
if cfg.IsSameStorage(neq) {
199+
t.Errorf("storage instances has different bucket: cfg=%+v, eq=%+v", cfg, neq)
200+
}
201+
202+
neq = cfg.Clone()
203+
neq.Prefix = "p2"
204+
if cfg.IsSameStorage(neq) {
205+
t.Errorf("storage instances has different prefix: cfg=%+v, eq=%+v", cfg, neq)
206+
}
207+
})
154208
}
155209

156210
func boolPtr(b bool) *bool {

pbm/log/discard.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ func (discardEventImpl) Info(msg string, args ...any) {}
6666
func (discardEventImpl) Warning(msg string, args ...any) {}
6767
func (discardEventImpl) Error(msg string, args ...any) {}
6868
func (discardEventImpl) Fatal(msg string, args ...any) {}
69+
func (discardEventImpl) GetLogger() Logger { return &discardLoggerImpl{} }

pbm/log/event.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,26 @@ type eventImpl struct {
1313
opid string
1414
}
1515

16-
func (e *eventImpl) Debug(msg string, args ...interface{}) {
16+
func (e *eventImpl) Debug(msg string, args ...any) {
1717
e.l.Debug(e.typ, e.obj, e.opid, e.ep, msg, args...)
1818
}
1919

20-
func (e *eventImpl) Info(msg string, args ...interface{}) {
20+
func (e *eventImpl) Info(msg string, args ...any) {
2121
e.l.Info(e.typ, e.obj, e.opid, e.ep, msg, args...)
2222
}
2323

24-
func (e *eventImpl) Warning(msg string, args ...interface{}) {
24+
func (e *eventImpl) Warning(msg string, args ...any) {
2525
e.l.Warning(e.typ, e.obj, e.opid, e.ep, msg, args...)
2626
}
2727

28-
func (e *eventImpl) Error(msg string, args ...interface{}) {
28+
func (e *eventImpl) Error(msg string, args ...any) {
2929
e.l.Error(e.typ, e.obj, e.opid, e.ep, msg, args...)
3030
}
3131

32-
func (e *eventImpl) Fatal(msg string, args ...interface{}) {
32+
func (e *eventImpl) Fatal(msg string, args ...any) {
3333
e.l.Fatal(e.typ, e.obj, e.opid, e.ep, msg, args...)
3434
}
35+
36+
func (e *eventImpl) GetLogger() Logger {
37+
return e.l
38+
}

pbm/log/log.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type LogEvent interface {
4545
Warning(msg string, args ...any)
4646
Error(msg string, args ...any)
4747
Fatal(msg string, args ...any)
48+
GetLogger() Logger
4849
}
4950

5051
type Buffer interface {

pbm/storage/gcs/gcs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
)
1414

1515
const (
16+
gcsEndpointURL = "storage.googleapis.com"
1617
defaultMaxObjSizeGB = 5018 // 4.9 TB
1718
)
1819

pbm/storage/gcs/hmac_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func newHMACClient(opts *Config, l log.LogEvent) (*hmacClient, error) {
3636
}
3737
}
3838

39-
minioClient, err := minio.New("storage.googleapis.com", &minio.Options{
39+
minioClient, err := minio.New(gcsEndpointURL, &minio.Options{
4040
Creds: credentials.NewStaticV2(opts.Credentials.HMACAccessKey, opts.Credentials.HMACSecret, ""),
4141
})
4242
if err != nil {

0 commit comments

Comments
 (0)