syncbased: add flag to allow the creation of an initial database when
setting up a new service instance
This works similarly to how we set up the initial permissions the very
first time we run syncbased on a blank root dir.
MultiPart: 2/2
Change-Id: I8914c135556aef49586faa4f1ff3b65e9b7c6963
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index 7941db5..fde8964 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -60,7 +60,7 @@
// ServiceOptions configures a service.
type ServiceOptions struct {
- // Service-level permissions. Used only when creating a brand-new storage
+ // Service-level permissions. Used only when creating a brand new storage
// instance.
Perms access.Permissions
// Root dir for data storage. If empty, we write to a fresh directory created
@@ -74,6 +74,9 @@
// Whether to run in development mode; required for RPCs such as
// Service.DevModeUpdateVClock.
DevMode bool
+ // InitialDB, if not blank, specifies an initial database to create when
+ // creating a brand new storage instance.
+ InitialDB wire.Id
}
// defaultPerms returns a permissions object that grants all permissions to the
@@ -143,6 +146,7 @@
}
var sd ServiceData
+ newService := false
if err := store.Get(ctx, st, s.stKey(), &sd); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return nil, err
@@ -166,6 +170,7 @@
return nil, verror.New(verror.ErrInternal, ctx, err)
}
} else {
+ newService = true
perms := opts.Perms
// Service does not exist.
if perms == nil {
@@ -187,6 +192,13 @@
return nil, err
}
+ if newService && opts.InitialDB != (wire.Id{}) {
+ ctx.Info("Creating initial database:", opts.InitialDB)
+ if err := s.createDatabase(ctx, nil, opts.InitialDB, nil, nil); err != nil {
+ return nil, err
+ }
+ }
+
// With Sync and the pre-existing DBs initialized, the store can start a
// Sync watcher for each DB store, similar to the flow of a DB creation.
for _, d := range s.dbs {
@@ -403,8 +415,17 @@
}
// 1. Check serviceData perms.
- sData := &ServiceData{}
- if err := util.GetWithAuth(ctx, call, s.st, s.stKey(), sData); err != nil {
+ getServiceData := func() (sd *ServiceData, err error) {
+ sd = new(ServiceData)
+ if call != nil {
+ err = util.GetWithAuth(ctx, call, s.st, s.stKey(), sd)
+ } else {
+ err = store.Get(ctx, s.st, s.stKey(), sd)
+ }
+ return
+ }
+ sData, err := getServiceData()
+ if err != nil {
return err
}
@@ -458,11 +479,9 @@
// Note: To avoid a race, we must re-check service perms, and make sure the
// perms version hasn't changed, inside the transaction that makes the new
// database visible.
- sDataRepeat := &ServiceData{}
- if err := util.GetWithAuth(ctx, call, s.st, s.stKey(), sDataRepeat); err != nil {
+ if sDataRepeat, err := getServiceData(); err != nil {
return err
- }
- if sData.Version != sDataRepeat.Version {
+ } else if sData.Version != sDataRepeat.Version {
return verror.NewErrBadVersion(ctx)
}
// Check for "database already exists".
diff --git a/services/syncbase/syncbased/doc.go b/services/syncbase/syncbased/doc.go
index 43589e4..9237fcf 100644
--- a/services/syncbase/syncbased/doc.go
+++ b/services/syncbase/syncbased/doc.go
@@ -21,6 +21,11 @@
-engine=
Storage engine to use: memstore or leveldb. If empty, we use the default
storage engine, currently leveldb.
+ -initial-db=
+ If specified, a new database with the given id is created when setting up a
+ brand new storage instance. Permissions for the database will be the service
+ permissions. Format must conform to v.io/services/syncbase.Id.String:
+ blessing,name
-name=
Name to mount at.
-root-dir=
diff --git a/services/syncbase/syncbaselib/opts.go b/services/syncbase/syncbaselib/opts.go
index eb6bfa3..1626851 100644
--- a/services/syncbase/syncbaselib/opts.go
+++ b/services/syncbase/syncbaselib/opts.go
@@ -15,6 +15,7 @@
SkipPublishInNh bool
DevMode bool
CpuProfile string
+ InitialDB string
}
// Note: Where possible, we have flag default values be zero values, so that
@@ -26,4 +27,5 @@
f.BoolVar(&o.SkipPublishInNh, "skip-publish-in-nh", false, "Whether to skip publishing in the neighborhood.")
f.BoolVar(&o.DevMode, "dev", false, "Whether to run in development mode; required for RPCs such as Service.DevModeUpdateVClock.")
f.StringVar(&o.CpuProfile, "cpuprofile", "", "If specified, write the cpu profile to the given filename.")
+ f.StringVar(&o.InitialDB, "initial-db", "", "If specified, a new database with the given id is created when setting up a brand new storage instance. Permissions for the database will be the service permissions. Format must conform to v.io/services/syncbase.Id.String: blessing,name")
}
diff --git a/services/syncbase/syncbaselib/serve.go b/services/syncbase/syncbaselib/serve.go
index 8a988b4..dea394a 100644
--- a/services/syncbase/syncbaselib/serve.go
+++ b/services/syncbase/syncbaselib/serve.go
@@ -13,6 +13,8 @@
"v.io/v23/context"
"v.io/v23/options"
"v.io/v23/rpc"
+ wire "v.io/v23/services/syncbase"
+ pubutil "v.io/v23/syncbase/util"
"v.io/x/ref/lib/dispatcher"
"v.io/x/ref/lib/security/securityflag"
"v.io/x/ref/services/syncbase/server"
@@ -54,12 +56,22 @@
if perms != nil {
ctx.Infof("Read permissions from command line flag: %v", server.PermsString(ctx, perms))
}
+ var initialDB wire.Id
+ if opts.InitialDB != "" {
+ if initialDB, err = wire.ParseId(opts.InitialDB); err != nil {
+ ctx.Fatalf("ParseId(%s) failed: %v", opts.InitialDB, err)
+ }
+ if err := pubutil.ValidateId(initialDB); err != nil {
+ ctx.Fatalf("ValidateId(%v) failed: %v", initialDB, err)
+ }
+ }
service, err := server.NewService(ctx, server.ServiceOptions{
Perms: perms,
RootDir: opts.RootDir,
Engine: opts.Engine,
SkipPublishInNh: opts.SkipPublishInNh,
DevMode: opts.DevMode,
+ InitialDB: initialDB,
})
if err != nil {
ctx.Fatal("server.NewService() failed: ", err)