Skip to content

Commit e47415f

Browse files
committed
Support users and roles for selective backup
1 parent 6809d65 commit e47415f

File tree

10 files changed

+240
-87
lines changed

10 files changed

+240
-87
lines changed

cmd/pbm/backup.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type backupOpts struct {
3838
wait bool
3939
waitTime time.Duration
4040
externList bool
41+
usersAndRoles bool
4142

4243
numParallelColls int32
4344
}
@@ -108,6 +109,9 @@ func runBackup(
108109
if len(nss) != 0 && b.typ != string(defs.LogicalBackup) {
109110
return nil, errors.New("--ns flag is only allowed for logical backup")
110111
}
112+
if err := util.ValidateUsersAndRolesOpt(b.usersAndRoles, nss); err != nil {
113+
return nil, errors.Wrap(err, "parse --with-users-and-roles option")
114+
}
111115

112116
if err := topo.CheckTopoForBackup(ctx, conn, defs.BackupType(b.typ)); err != nil {
113117
return nil, errors.Wrap(err, "backup pre-check")
@@ -145,6 +149,7 @@ func runBackup(
145149
IncrBase: b.base,
146150
Name: b.name,
147151
Namespaces: nss,
152+
UsersAndRoles: b.usersAndRoles,
148153
Compression: compression,
149154
CompressionLevel: level,
150155
NumParallelColls: numParallelColls,
@@ -343,6 +348,7 @@ type bcpDesc struct {
343348
LastWriteTime string `json:"last_write_time" yaml:"last_write_time"`
344349
LastTransitionTime string `json:"last_transition_time" yaml:"last_transition_time"`
345350
Namespaces []string `json:"namespaces,omitempty" yaml:"namespaces,omitempty"`
351+
SelUserAndRoles bool `json:"sel_user_and_roles" yaml:"sel_user_and_roles"`
346352
MongoVersion string `json:"mongodb_version" yaml:"mongodb_version"`
347353
FCV string `json:"fcv" yaml:"fcv"`
348354
PBMVersion string `json:"pbm_version" yaml:"pbm_version"`
@@ -434,6 +440,7 @@ func describeBackup(
434440
OPID: bcp.OPID,
435441
Type: bcp.Type,
436442
Namespaces: bcp.Namespaces,
443+
SelUserAndRoles: bcp.SelUsersAndRoles,
437444
MongoVersion: bcp.MongoVersion,
438445
FCV: bcp.FCV,
439446
PBMVersion: bcp.PBMVersion,

cmd/pbm/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ func (app *pbmApp) buildBackupCmd() *cobra.Command {
295295
backupCmd.Flags().StringVar(
296296
&backupOptions.ns, "ns", "", `Namespaces to backup (e.g. "db.*", "db.collection"). If not set, backup all ("*.*")`,
297297
)
298+
backupCmd.Flags().BoolVar(
299+
&backupOptions.usersAndRoles, "with-users-and-roles", false,
300+
"Includes users and roles for selected database (--ns flag)",
301+
)
298302
backupCmd.Flags().BoolVarP(
299303
&backupOptions.wait, "wait", "w", false, "Wait for the backup to finish",
300304
)

cmd/pbm/restore.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func runRestore(
138138
if err := validateNSFromNSTo(o); err != nil {
139139
return nil, errors.Wrap(err, "parse --ns-from and --ns-to options")
140140
}
141-
if err := validateRestoreUsersAndRoles(o.usersAndRoles, nss); err != nil {
141+
if err := util.ValidateUsersAndRolesOpt(o.usersAndRoles, nss); err != nil {
142142
return nil, errors.Wrap(err, "parse --with-users-and-roles option")
143143
}
144144
if err := validateFallbackOpts(o); err != nil {
@@ -805,19 +805,6 @@ func describeRestore(
805805
return res, nil
806806
}
807807

808-
func validateRestoreUsersAndRoles(usersAndRoles bool, nss []string) error {
809-
if !util.IsSelective(nss) && usersAndRoles {
810-
return errors.New("Including users and roles are only allowed for selected database " +
811-
"(use --ns flag for selective backup)")
812-
}
813-
if len(nss) >= 1 && util.ContainsSpecifiedColl(nss) && usersAndRoles {
814-
return errors.New("Including users and roles are not allowed for specific collection. " +
815-
"Use --ns='db.*' to specify the whole database instead.")
816-
}
817-
818-
return nil
819-
}
820-
821808
func validateNSFromNSTo(o *restoreOpts) error {
822809
if o.nsFrom == "" && o.nsTo == "" {
823810
return nil

pbm/backup/backup.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,12 @@ func (b *Backup) Init(
110110
}
111111

112112
meta := &BackupMeta{
113-
Type: b.typ,
114-
OPID: opid.String(),
115-
Name: bcp.Name,
116-
Namespaces: bcp.Namespaces,
117-
Compression: bcp.Compression,
113+
Type: b.typ,
114+
OPID: opid.String(),
115+
Name: bcp.Name,
116+
Namespaces: bcp.Namespaces,
117+
SelUsersAndRoles: bcp.UsersAndRoles,
118+
Compression: bcp.Compression,
118119
Store: Storage{
119120
Name: b.config.Name,
120121
IsProfile: b.config.IsProfile,

pbm/backup/logical.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ func (b *Backup) doLogical(
112112
// ensure slicer is stopped in any case (done, error or canceled)
113113
defer stopOplogSlicer() //nolint:errcheck
114114

115-
if !util.IsSelective(bcp.Namespaces) {
115+
if !util.IsSelective(bcp.Namespaces) || bcp.UsersAndRoles {
116116
// Save users and roles to the tmp collections so the restore would copy that data
117117
// to the system collections. Have to do this because of issues with the restore and preserverUUID.
118118
// see: https://jira.percona.com/browse/PBM-636 and comments
119-
lw, err := copyUsersNRolles(ctx, b.brief.URI)
119+
lw, err := copyUsersNRolles(ctx, b.brief.URI, bcp.Namespaces)
120120
if err != nil {
121121
return errors.Wrap(err, "copy users and roles for the restore")
122122
}
@@ -152,6 +152,12 @@ func (b *Backup) doLogical(
152152
nsFilter := archive.DefaultNSFilter
153153
docFilter := archive.DefaultDocFilter
154154
if util.IsSelective(bcp.Namespaces) {
155+
if bcp.UsersAndRoles {
156+
bcp.Namespaces = append(bcp.Namespaces,
157+
defs.DB+"."+defs.TmpUsersCollection,
158+
defs.DB+"."+defs.TmpRolesCollection,
159+
)
160+
}
155161
if inf.IsConfigSrv() {
156162
chunkSelector, err := createBackupChunkSelector(ctx, b.leadConn, bcp.Namespaces)
157163
if err != nil {
@@ -286,7 +292,7 @@ func waitForWrite(ctx context.Context, m *mongo.Client, ts primitive.Timestamp)
286292
}
287293

288294
//nolint:nonamedreturns
289-
func copyUsersNRolles(ctx context.Context, uri string) (lastWrite primitive.Timestamp, err error) {
295+
func copyUsersNRolles(ctx context.Context, uri string, nss []string) (lastWrite primitive.Timestamp, err error) {
290296
cn, err := connect.MongoConnect(ctx, uri)
291297
if err != nil {
292298
return lastWrite, errors.Wrap(err, "connect to primary")
@@ -298,18 +304,19 @@ func copyUsersNRolles(ctx context.Context, uri string) (lastWrite primitive.Time
298304
return lastWrite, errors.Wrap(err, "drop tmp collections before copy")
299305
}
300306

307+
filter := util.MakeDBMatchFilter(nss)
301308
err = copyColl(ctx,
302309
cn.Database("admin").Collection("system.roles"),
303310
cn.Database(defs.DB).Collection(defs.TmpRolesCollection),
304-
bson.M{},
311+
filter,
305312
)
306313
if err != nil {
307314
return lastWrite, errors.Wrap(err, "copy admin.system.roles")
308315
}
309316
err = copyColl(ctx,
310317
cn.Database("admin").Collection("system.users"),
311318
cn.Database(defs.DB).Collection(defs.TmpUsersCollection),
312-
bson.M{},
319+
filter,
313320
)
314321
if err != nil {
315322
return lastWrite, errors.Wrap(err, "copy admin.system.users")

pbm/backup/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type BackupMeta struct {
4040
ShardRemap map[string]string `bson:"shardRemap,omitempty" json:"shardRemap,omitempty"`
4141

4242
Namespaces []string `bson:"nss,omitempty" json:"nss,omitempty"`
43+
SelUsersAndRoles bool `bson:"sel_users_and_roles,omitempty" json:"sel_users_and_roles,omitempty"`
4344
Replsets []BackupReplset `bson:"replsets" json:"replsets"`
4445
Compression compress.CompressionType `bson:"compression" json:"compression"`
4546
Store Storage `bson:"store" json:"store"`

pbm/ctrl/cmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ type BackupCmd struct {
137137
NumParallelColls *int32 `bson:"numParallelColls,omitempty"`
138138
Filelist bool `bson:"filelist,omitempty"`
139139
Profile string `bson:"profile,omitempty"`
140+
UsersAndRoles bool `bson:"usersAndRoles,omitempty"`
140141
}
141142

142143
func (b BackupCmd) String() string {

pbm/restore/logical.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,16 @@ func (r *Restore) exit(ctx context.Context, err error) {
146146

147147
// resolveNamespace resolves final namespace(s) based on the backup namespace,
148148
// restore namespace, cloning options and option whether we should restore users&roles
149-
func resolveNamespace(nssBackup, nssRestore []string, cloneNS snapshot.CloneNS, usingUsersAndRoles bool) []string {
149+
func resolveNamespace(
150+
nssBackup, nssRestore []string,
151+
cloneNS snapshot.CloneNS,
152+
usingUsersAndRolesBackup, usingUsersAndRolesRestore bool,
153+
) []string {
150154
if cloneNS.IsSpecified() {
151155
return []string{cloneNS.FromNS}
152156
}
153157
if util.IsSelective(nssRestore) {
154-
if usingUsersAndRoles {
158+
if usingUsersAndRolesRestore {
155159
var nss []string
156160
nss = append(nss, nssRestore...)
157161
nss = append(nss,
@@ -164,23 +168,34 @@ func resolveNamespace(nssBackup, nssRestore []string, cloneNS snapshot.CloneNS,
164168
return nssRestore
165169
}
166170

171+
// Full restore from selective backup should include users & roles
172+
if util.IsSelective(nssBackup) && usingUsersAndRolesBackup {
173+
var nss []string
174+
nss = append(nss, nssBackup...)
175+
nss = append(nss,
176+
defs.DB+"."+defs.TmpUsersCollection,
177+
defs.DB+"."+defs.TmpRolesCollection,
178+
)
179+
return nss
180+
}
181+
167182
return nssBackup
168183
}
169184

170185
// shouldRestoreUsersAndRoles determines whether user&roles should be restored from the backup
171186
func shouldRestoreUsersAndRoles(
172187
nssBackup, nssRestore []string,
173188
cloneNS snapshot.CloneNS,
174-
usingUsersAndRoles bool,
189+
usingUsersAndRolesBackup, usingUsersAndRolesRestore bool,
175190
) restoreUsersAndRolesOption {
176191
if cloneNS.IsSpecified() {
177192
return false
178193
}
179-
if util.IsSelective(nssBackup) {
194+
if util.IsSelective(nssBackup) && !usingUsersAndRolesBackup {
180195
return false
181196
}
182197
if util.IsSelective(nssRestore) {
183-
return restoreUsersAndRolesOption(usingUsersAndRoles)
198+
return restoreUsersAndRolesOption(usingUsersAndRolesRestore)
184199
}
185200

186201
return true
@@ -218,11 +233,13 @@ func (r *Restore) Snapshot(
218233
bcp.Namespaces,
219234
cmd.Namespaces,
220235
cloneNS,
236+
bcp.SelUsersAndRoles,
221237
cmd.UsersAndRoles)
222238
usersAndRolesOpt := shouldRestoreUsersAndRoles(
223239
bcp.Namespaces,
224240
cmd.Namespaces,
225241
cloneNS,
242+
bcp.SelUsersAndRoles,
226243
cmd.UsersAndRoles)
227244

228245
err = setRestoreBackup(ctx, r.leadConn, r.name, cmd.BackupName, nss)
@@ -362,11 +379,13 @@ func (r *Restore) PITR(
362379
bcp.Namespaces,
363380
cmd.Namespaces,
364381
cloneNS,
382+
bcp.SelUsersAndRoles,
365383
cmd.UsersAndRoles)
366384
usersAndRolesOpt := shouldRestoreUsersAndRoles(
367385
bcp.Namespaces,
368386
cmd.Namespaces,
369387
cloneNS,
388+
bcp.SelUsersAndRoles,
370389
cmd.UsersAndRoles)
371390

372391
if r.nodeInfo.IsLeader() {

0 commit comments

Comments
 (0)