blob: 1b5217d1673614ca059da74c5ab4772df0db4cdf [file] [log] [blame]
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package server
6
7import (
8 "sync"
9
10 wire "v.io/syncbase/v23/services/syncbase"
Adam Sadovskybc00bd62015-05-22 12:50:03 -070011 "v.io/syncbase/x/ref/services/syncbase/server/interfaces"
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070012 "v.io/syncbase/x/ref/services/syncbase/server/nosql"
13 "v.io/syncbase/x/ref/services/syncbase/server/util"
14 "v.io/syncbase/x/ref/services/syncbase/store"
15 "v.io/v23/context"
Adam Sadovsky49261192015-05-19 17:39:59 -070016 "v.io/v23/naming"
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070017 "v.io/v23/rpc"
18 "v.io/v23/security/access"
19 "v.io/v23/verror"
20)
21
22type app struct {
23 name string
24 s *service
25 // The fields below are initialized iff this app exists.
26 // Guards the fields below. Held during database Create, Delete, and
27 // SetPermissions.
28 mu sync.Mutex
Adam Sadovskybc00bd62015-05-22 12:50:03 -070029 dbs map[string]interfaces.Database
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070030}
31
32var (
33 _ wire.AppServerMethods = (*app)(nil)
Adam Sadovskybc00bd62015-05-22 12:50:03 -070034 _ interfaces.App = (*app)(nil)
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070035 _ util.Layer = (*app)(nil)
36)
37
38////////////////////////////////////////
39// RPC methods
40
41// TODO(sadovsky): Require the app name to match the client's blessing name.
42// I.e. reserve names at the app level of the hierarchy.
43func (a *app) Create(ctx *context.T, call rpc.ServerCall, perms access.Permissions) error {
44 // This app does not yet exist; a is just an ephemeral handle that holds
45 // {name string, s *service}. a.s.createApp will create a new app handle and
46 // store it in a.s.apps[a.name].
47 return a.s.createApp(ctx, call, a.name, perms)
48}
49
50func (a *app) Delete(ctx *context.T, call rpc.ServerCall) error {
51 return a.s.deleteApp(ctx, call, a.name)
52}
53
54func (a *app) SetPermissions(ctx *context.T, call rpc.ServerCall, perms access.Permissions, version string) error {
55 return a.s.setAppPerms(ctx, call, a.name, perms, version)
56}
57
58func (a *app) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
59 data := &appData{}
60 if err := util.Get(ctx, call, a.s.st, a, data); err != nil {
61 return nil, "", err
62 }
63 return data.Perms, util.FormatVersion(data.Version), nil
64}
65
Adam Sadovsky49261192015-05-19 17:39:59 -070066func (a *app) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
67 // Check perms.
68 sn := a.s.st.NewSnapshot()
69 if err := util.Get(ctx, call, sn, a, &appData{}); err != nil {
70 sn.Close()
71 return nil, err
72 }
73 return util.Glob(ctx, call, pattern, sn, util.JoinKeyParts(util.DbInfoPrefix, a.name))
74}
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070075
76////////////////////////////////////////
Adam Sadovskybc00bd62015-05-22 12:50:03 -070077// interfaces.App methods
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070078
Adam Sadovskybc00bd62015-05-22 12:50:03 -070079func (a *app) NoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) (interfaces.Database, error) {
Adam Sadovskyf3b7abc2015-05-04 15:33:22 -070080 // TODO(sadovsky): Record storage engine config (e.g. LevelDB directory) in
81 // dbInfo, and add API for opening and closing storage engines.
82 a.mu.Lock()
83 defer a.mu.Unlock()
84 d, ok := a.dbs[dbName]
85 if !ok {
86 return nil, verror.New(verror.ErrNoExistOrNoAccess, ctx, dbName)
87 }
88 return d, nil
89}
90
91func (a *app) CreateNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string, perms access.Permissions) error {
92 // TODO(sadovsky): Crash if any step fails, and use WAL to ensure that if we
93 // crash, upon restart we execute any remaining steps before we start handling
94 // client requests.
95 //
96 // Steps:
97 // 1. Check appData perms, create dbInfo record.
98 // 2. Initialize database.
99 // 3. Flip dbInfo.Initialized to true. <===== CHANGE BECOMES VISIBLE
100 a.mu.Lock()
101 defer a.mu.Unlock()
102 if _, ok := a.dbs[dbName]; ok {
103 // TODO(sadovsky): Should this be ErrExistOrNoAccess, for privacy?
104 return verror.New(verror.ErrExist, ctx, dbName)
105 }
106
107 // 1. Check appData perms, create dbInfo record.
108 aData := &appData{}
109 if err := store.RunInTransaction(a.s.st, func(st store.StoreReadWriter) error {
110 // Check appData perms.
111 if err := util.Get(ctx, call, st, a, aData); err != nil {
112 return err
113 }
114 // Check for "database already exists".
115 if _, err := a.getDbInfo(ctx, call, st, dbName); verror.ErrorID(err) != verror.ErrNoExistOrNoAccess.ID {
116 if err != nil {
117 return err
118 }
119 // TODO(sadovsky): Should this be ErrExistOrNoAccess, for privacy?
120 return verror.New(verror.ErrExist, ctx, dbName)
121 }
122 // Write new dbInfo.
123 info := &dbInfo{
124 Name: dbName,
125 }
126 return a.putDbInfo(ctx, call, st, dbName, info)
127 }); err != nil {
128 return err
129 }
130
131 // 2. Initialize database.
132 if perms == nil {
133 perms = aData.Perms
134 }
135 d, err := nosql.NewDatabase(ctx, call, a, dbName, perms)
136 if err != nil {
137 return err
138 }
139
140 // 3. Flip dbInfo.Initialized to true.
141 if err := store.RunInTransaction(a.s.st, func(st store.StoreReadWriter) error {
142 return a.updateDbInfo(ctx, call, st, dbName, func(info *dbInfo) error {
143 info.Initialized = true
144 return nil
145 })
146 }); err != nil {
147 return err
148 }
149
150 a.dbs[dbName] = d
151 return nil
152}
153
154func (a *app) DeleteNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error {
155 // TODO(sadovsky): Crash if any step fails, and use WAL to ensure that if we
156 // crash, upon restart we execute any remaining steps before we start handling
157 // client requests.
158 //
159 // Steps:
160 // 1. Check databaseData perms.
161 // 2. Flip dbInfo.Deleted to true. <===== CHANGE BECOMES VISIBLE
162 // 3. Delete database.
163 // 4. Delete dbInfo record.
164 a.mu.Lock()
165 defer a.mu.Unlock()
166 d, ok := a.dbs[dbName]
167 if !ok {
168 return verror.New(verror.ErrNoExistOrNoAccess, ctx, dbName)
169 }
170
171 // 1. Check databaseData perms.
172 if err := d.CheckPermsInternal(ctx, call); err != nil {
173 return err
174 }
175
176 // 2. Flip dbInfo.Deleted to true.
177 if err := store.RunInTransaction(a.s.st, func(st store.StoreReadWriter) error {
178 return a.updateDbInfo(ctx, call, st, dbName, func(info *dbInfo) error {
179 info.Deleted = true
180 return nil
181 })
182 }); err != nil {
183 return err
184 }
185
186 // 3. Delete database.
187 // TODO(sadovsky): Actually delete the database.
188
189 // 4. Delete dbInfo record.
190 if err := a.delDbInfo(ctx, call, a.s.st, dbName); err != nil {
191 return err
192 }
193
194 delete(a.dbs, dbName)
195 return nil
196}
197
198func (a *app) SetDatabasePerms(ctx *context.T, call rpc.ServerCall, dbName string, perms access.Permissions, version string) error {
199 a.mu.Lock()
200 defer a.mu.Unlock()
201 d, ok := a.dbs[dbName]
202 if !ok {
203 return verror.New(verror.ErrNoExistOrNoAccess, ctx, dbName)
204 }
205 return d.SetPermsInternal(ctx, call, perms, version)
206}
207
208////////////////////////////////////////
209// util.Layer methods
210
211func (a *app) Name() string {
212 return a.name
213}
214
215func (a *app) StKey() string {
216 return util.JoinKeyParts(util.AppPrefix, a.stKeyPart())
217}
218
219////////////////////////////////////////
220// Internal helpers
221
222func (a *app) stKeyPart() string {
223 return a.name
224}