blob: 2ea8bff5d62e95330a7b30356cf9fd5bb681866c [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -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
Todd Wang4ac9e652015-03-27 14:50:47 -07005package main
Jiri Simsa5293dcb2014-05-10 09:56:38 -07006
7import (
Robert Kroegerc66b3872015-04-22 14:24:32 -07008 "sort"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07009 "strings"
10
Robert Kroegerf9536ac2015-04-03 16:30:44 -070011 "v.io/v23/context"
Robin Thellendf15f2952015-07-17 21:49:58 -070012 "v.io/v23/glob"
Jiri Simsa6ac95222015-02-23 16:11:49 -080013 "v.io/v23/naming"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070014 "v.io/v23/rpc"
Robert Kroegerf9536ac2015-04-03 16:30:44 -070015 "v.io/v23/security"
Todd Wang387d8a42015-03-30 17:09:05 -070016 "v.io/v23/security/access"
Todd Wang94c9d0b2015-04-01 14:27:00 -070017 "v.io/v23/services/application"
Jiri Simsa6ac95222015-02-23 16:11:49 -080018 "v.io/v23/verror"
Jiri Simsa87d884d2015-06-18 10:25:54 -070019 "v.io/x/lib/set"
Jiri Simsa87d884d2015-06-18 10:25:54 -070020 "v.io/x/ref/services/internal/fs"
21 "v.io/x/ref/services/internal/pathperms"
22 "v.io/x/ref/services/repository"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070023)
24
Robin Thellend9bc8fcb2014-11-17 10:23:04 -080025// appRepoService implements the Application repository interface.
26type appRepoService struct {
Robert Kroegerd9c34882014-08-22 11:16:38 -070027 // store is the storage server used for storing application
28 // metadata.
Robin Thellend9bc8fcb2014-11-17 10:23:04 -080029 // All objects share the same Memstore.
Robert Kroegerd9c34882014-08-22 11:16:38 -070030 store *fs.Memstore
Robin Thellend9bc8fcb2014-11-17 10:23:04 -080031 // storeRoot is a name in the directory under which all data will be
32 // stored.
Adam Sadovskyffac12c2014-08-20 14:23:16 -070033 storeRoot string
Robin Thellend9bc8fcb2014-11-17 10:23:04 -080034 // suffix is the name of the application object.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070035 suffix string
36}
37
Todd Wang159f6ee2015-04-02 18:57:46 -070038const pkgPath = "v.io/x/ref/services/application/applicationd/"
Todd Wang34ed4c62014-11-26 15:15:52 -080039
Jiri Simsa5293dcb2014-05-10 09:56:38 -070040var (
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080041 ErrInvalidSuffix = verror.Register(pkgPath+".InvalidSuffix", verror.NoRetry, "{1:}{2:} invalid suffix{:_}")
42 ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "{1:}{2:} operation failed{:_}")
Robert Kroegerf9536ac2015-04-03 16:30:44 -070043 ErrNotAuthorized = verror.Register(pkgPath+".errNotAuthorized", verror.NoRetry, "{1:}{2:} none of the client's blessings are valid {:_}")
Jiri Simsa5293dcb2014-05-10 09:56:38 -070044)
45
Robin Thellend9bc8fcb2014-11-17 10:23:04 -080046// NewApplicationService returns a new Application service implementation.
Bogdan Capritae96cd042015-02-03 17:32:57 -080047func NewApplicationService(store *fs.Memstore, storeRoot, suffix string) repository.ApplicationServerMethods {
Robin Thellend9bc8fcb2014-11-17 10:23:04 -080048 return &appRepoService{store: store, storeRoot: storeRoot, suffix: suffix}
Jiri Simsa5293dcb2014-05-10 09:56:38 -070049}
50
Robert Kroegerf9536ac2015-04-03 16:30:44 -070051func parse(ctx *context.T, suffix string) (string, string, error) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070052 tokens := strings.Split(suffix, "/")
53 switch len(tokens) {
54 case 2:
55 return tokens[0], tokens[1], nil
56 case 1:
57 return tokens[0], "", nil
58 default:
Robert Kroegerf9536ac2015-04-03 16:30:44 -070059 return "", "", verror.New(ErrInvalidSuffix, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070060 }
61}
62
Todd Wang54feabe2015-04-15 23:38:26 -070063func (i *appRepoService) Match(ctx *context.T, call rpc.ServerCall, profiles []string) (application.Envelope, error) {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -070064 ctx.VI(0).Infof("%v.Match(%v)", i.suffix, profiles)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070065 empty := application.Envelope{}
Todd Wang54feabe2015-04-15 23:38:26 -070066 name, version, err := parse(ctx, i.suffix)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070067 if err != nil {
68 return empty, err
69 }
Robert Kroegerd9c34882014-08-22 11:16:38 -070070
71 i.store.Lock()
72 defer i.store.Unlock()
73
Robert Kroegerc66b3872015-04-22 14:24:32 -070074 if version == "" {
Robert Kroeger78947322015-06-24 14:12:17 -070075 versions, err := i.allAppVersionsForProfiles(name, profiles)
Robert Kroegerc66b3872015-04-22 14:24:32 -070076 if err != nil {
77 return empty, err
78 }
79 if len(versions) < 1 {
80 return empty, verror.New(ErrInvalidSuffix, ctx)
81 }
82 sort.Strings(versions)
83 version = versions[len(versions)-1]
84 }
85
Jiri Simsa5293dcb2014-05-10 09:56:38 -070086 for _, profile := range profiles {
Ken Ashcraft7ca37d92014-08-12 17:46:43 -070087 path := naming.Join("/applications", name, profile, version)
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -080088 entry, err := i.store.BindObject(path).Get(call)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070089 if err != nil {
90 continue
91 }
92 envelope, ok := entry.Value.(application.Envelope)
93 if !ok {
94 continue
95 }
96 return envelope, nil
97 }
Todd Wang54feabe2015-04-15 23:38:26 -070098 return empty, verror.New(verror.ErrNoExist, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070099}
100
Todd Wang54feabe2015-04-15 23:38:26 -0700101func (i *appRepoService) Put(ctx *context.T, call rpc.ServerCall, profiles []string, envelope application.Envelope) error {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700102 ctx.VI(0).Infof("%v.Put(%v, %v)", i.suffix, profiles, envelope)
Todd Wang54feabe2015-04-15 23:38:26 -0700103 name, version, err := parse(ctx, i.suffix)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700104 if err != nil {
105 return err
106 }
107 if version == "" {
Todd Wang54feabe2015-04-15 23:38:26 -0700108 return verror.New(ErrInvalidSuffix, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700109 }
Robert Kroegerd9c34882014-08-22 11:16:38 -0700110 i.store.Lock()
111 defer i.store.Unlock()
112 // Transaction is rooted at "", so tname == tid.
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800113 tname, err := i.store.BindTransactionRoot("").CreateTransaction(call)
Robert Kroegerd9c34882014-08-22 11:16:38 -0700114 if err != nil {
115 return err
116 }
117
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700118 // Only add a Permissions value if there is not already one present.
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700119 apath := naming.Join("/acls", name, "data")
120 aobj := i.store.BindObject(apath)
121 if _, err := aobj.Get(call); verror.ErrorID(err) == fs.ErrNotInMemStore.ID {
Todd Wang4264e4b2015-04-16 22:43:40 -0700122 rb, _ := security.RemoteBlessingNames(ctx, call.Security())
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700123 if len(rb) == 0 {
124 // None of the client's blessings are valid.
Todd Wang54feabe2015-04-15 23:38:26 -0700125 return verror.New(ErrNotAuthorized, ctx)
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700126 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700127 newperms := pathperms.PermissionsForBlessings(rb)
128 if _, err := aobj.Put(nil, newperms); err != nil {
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700129 return err
130 }
131 }
132
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700133 for _, profile := range profiles {
Robert Kroegerd9c34882014-08-22 11:16:38 -0700134 path := naming.Join(tname, "/applications", name, profile, version)
135
136 object := i.store.BindObject(path)
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800137 _, err := object.Put(call, envelope)
Robert Kroegerd9c34882014-08-22 11:16:38 -0700138 if err != nil {
Todd Wang54feabe2015-04-15 23:38:26 -0700139 return verror.New(ErrOperationFailed, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700140 }
141 }
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800142 if err := i.store.BindTransaction(tname).Commit(call); err != nil {
Todd Wang54feabe2015-04-15 23:38:26 -0700143 return verror.New(ErrOperationFailed, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700144 }
145 return nil
146}
147
Todd Wang54feabe2015-04-15 23:38:26 -0700148func (i *appRepoService) Remove(ctx *context.T, call rpc.ServerCall, profile string) error {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700149 ctx.VI(0).Infof("%v.Remove(%v)", i.suffix, profile)
Todd Wang54feabe2015-04-15 23:38:26 -0700150 name, version, err := parse(ctx, i.suffix)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700151 if err != nil {
152 return err
153 }
Robert Kroegerd9c34882014-08-22 11:16:38 -0700154 i.store.Lock()
155 defer i.store.Unlock()
156 // Transaction is rooted at "", so tname == tid.
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800157 tname, err := i.store.BindTransactionRoot("").CreateTransaction(call)
Robert Kroegerd9c34882014-08-22 11:16:38 -0700158 if err != nil {
159 return err
160 }
161 path := naming.Join(tname, "/applications", name, profile)
162 if version != "" {
163 path += "/" + version
164 }
165 object := i.store.BindObject(path)
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800166 found, err := object.Exists(call)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700167 if err != nil {
Todd Wang54feabe2015-04-15 23:38:26 -0700168 return verror.New(ErrOperationFailed, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700169 }
170 if !found {
Todd Wang54feabe2015-04-15 23:38:26 -0700171 return verror.New(verror.ErrNoExist, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700172 }
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800173 if err := object.Remove(call); err != nil {
Todd Wang54feabe2015-04-15 23:38:26 -0700174 return verror.New(ErrOperationFailed, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700175 }
Matt Rosencrantz9dce9b22015-03-02 10:48:37 -0800176 if err := i.store.BindTransaction(tname).Commit(call); err != nil {
Todd Wang54feabe2015-04-15 23:38:26 -0700177 return verror.New(ErrOperationFailed, ctx)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700178 }
179 return nil
180}
Robin Thellend0155a372014-11-11 17:30:11 -0800181
Robin Thellend9bc8fcb2014-11-17 10:23:04 -0800182func (i *appRepoService) allApplications() ([]string, error) {
Robin Thellend0155a372014-11-11 17:30:11 -0800183 apps, err := i.store.BindObject("/applications").Children()
184 if err != nil {
185 return nil, err
186 }
187 return apps, nil
188}
189
Robert Kroeger78947322015-06-24 14:12:17 -0700190func (i *appRepoService) allAppVersionsForProfiles(appName string, profiles []string) ([]string, error) {
Robin Thellend0155a372014-11-11 17:30:11 -0800191 uniqueVersions := make(map[string]struct{})
192 for _, profile := range profiles {
193 versions, err := i.store.BindObject(naming.Join("/applications", appName, profile)).Children()
Robert Kroeger78947322015-06-24 14:12:17 -0700194 if verror.ErrorID(err) == verror.ErrNoExist.ID {
195 continue
196 } else if err != nil {
Robin Thellend0155a372014-11-11 17:30:11 -0800197 return nil, err
198 }
Jiri Simsa87d884d2015-06-18 10:25:54 -0700199 set.String.Union(uniqueVersions, set.String.FromSlice(versions))
Robin Thellend0155a372014-11-11 17:30:11 -0800200 }
Jiri Simsa87d884d2015-06-18 10:25:54 -0700201 return set.String.ToSlice(uniqueVersions), nil
Robin Thellend0155a372014-11-11 17:30:11 -0800202}
203
Robert Kroeger78947322015-06-24 14:12:17 -0700204func (i *appRepoService) allAppVersions(appName string) ([]string, error) {
205 profiles, err := i.store.BindObject(naming.Join("/applications", appName)).Children()
206 if err != nil {
207 return nil, err
208 }
209 return i.allAppVersionsForProfiles(appName, profiles)
210}
211
Robin Thellendf15f2952015-07-17 21:49:58 -0700212func (i *appRepoService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700213 ctx.VI(0).Infof("%v.GlobChildren__()", i.suffix)
Robin Thellend0155a372014-11-11 17:30:11 -0800214 i.store.Lock()
215 defer i.store.Unlock()
216
217 var elems []string
218 if i.suffix != "" {
219 elems = strings.Split(i.suffix, "/")
220 }
221
Robin Thellend8e9cc242014-11-26 09:43:10 -0800222 var results []string
223 var err error
Robin Thellend0155a372014-11-11 17:30:11 -0800224 switch len(elems) {
225 case 0:
Robin Thellend8e9cc242014-11-26 09:43:10 -0800226 results, err = i.allApplications()
227 if err != nil {
Robin Thellendf15f2952015-07-17 21:49:58 -0700228 return err
Robin Thellend8e9cc242014-11-26 09:43:10 -0800229 }
Robin Thellend0155a372014-11-11 17:30:11 -0800230 case 1:
Robin Thellend8e9cc242014-11-26 09:43:10 -0800231 results, err = i.allAppVersions(elems[0])
232 if err != nil {
Robin Thellendf15f2952015-07-17 21:49:58 -0700233 return err
Robin Thellend8e9cc242014-11-26 09:43:10 -0800234 }
Robin Thellend0155a372014-11-11 17:30:11 -0800235 case 2:
236 versions, err := i.allAppVersions(elems[0])
237 if err != nil {
Robin Thellendf15f2952015-07-17 21:49:58 -0700238 return err
Robin Thellend0155a372014-11-11 17:30:11 -0800239 }
240 for _, v := range versions {
241 if v == elems[1] {
Robin Thellendf15f2952015-07-17 21:49:58 -0700242 return nil
Robin Thellend0155a372014-11-11 17:30:11 -0800243 }
244 }
Robin Thellendf15f2952015-07-17 21:49:58 -0700245 return verror.New(verror.ErrNoExist, nil)
Robin Thellend0155a372014-11-11 17:30:11 -0800246 default:
Robin Thellendf15f2952015-07-17 21:49:58 -0700247 return verror.New(verror.ErrNoExist, nil)
Robin Thellend0155a372014-11-11 17:30:11 -0800248 }
Robin Thellend8e9cc242014-11-26 09:43:10 -0800249
Robin Thellend8e9cc242014-11-26 09:43:10 -0800250 for _, r := range results {
Robin Thellendf15f2952015-07-17 21:49:58 -0700251 if m.Match(r) {
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700252 call.SendStream().Send(naming.GlobChildrenReplyName{Value: r})
Robin Thellendf15f2952015-07-17 21:49:58 -0700253 }
Robin Thellend8e9cc242014-11-26 09:43:10 -0800254 }
Robin Thellendf15f2952015-07-17 21:49:58 -0700255 return nil
Robin Thellend0155a372014-11-11 17:30:11 -0800256}
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800257
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700258func (i *appRepoService) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
Todd Wang54feabe2015-04-15 23:38:26 -0700259 name, _, err := parse(ctx, i.suffix)
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700260 if err != nil {
261 return nil, "", err
262 }
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800263 i.store.Lock()
264 defer i.store.Unlock()
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700265 path := naming.Join("/acls", name, "data")
266
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700267 perms, version, err = getPermissions(ctx, i.store, path)
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700268 if verror.ErrorID(err) == verror.ErrNoExist.ID {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700269 return pathperms.NilAuthPermissions(ctx, call.Security()), "", nil
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700270 }
271
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700272 return perms, version, err
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800273}
274
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700275func (i *appRepoService) SetPermissions(ctx *context.T, _ rpc.ServerCall, perms access.Permissions, version string) error {
Todd Wang54feabe2015-04-15 23:38:26 -0700276 name, _, err := parse(ctx, i.suffix)
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700277 if err != nil {
278 return err
279 }
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800280 i.store.Lock()
281 defer i.store.Unlock()
Robert Kroegerf9536ac2015-04-03 16:30:44 -0700282 path := naming.Join("/acls", name, "data")
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700283 return setPermissions(ctx, i.store, path, perms, version)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800284}
285
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700286// getPermissions fetches a Permissions out of the Memstore at the provided path.
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800287// path is expected to already have been cleaned by naming.Join or its ilk.
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700288func getPermissions(ctx *context.T, store *fs.Memstore, path string) (access.Permissions, string, error) {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800289 entry, err := store.BindObject(path).Get(nil)
290
Todd Wang8fa38762015-03-25 14:04:59 -0700291 if verror.ErrorID(err) == fs.ErrNotInMemStore.ID {
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700292 // No Permissions exists
Todd Wang4ac9e652015-03-27 14:50:47 -0700293 return nil, "", verror.New(verror.ErrNoExist, nil)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800294 } else if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700295 ctx.Errorf("getPermissions: internal failure in fs.Memstore")
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800296 return nil, "", err
297 }
298
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700299 perms, ok := entry.Value.(access.Permissions)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800300 if !ok {
301 return nil, "", err
302 }
303
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700304 version, err := pathperms.ComputeVersion(perms)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800305 if err != nil {
306 return nil, "", err
307 }
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700308 return perms, version, nil
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800309}
310
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700311// setPermissions writes a Permissions into the Memstore at the provided path.
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800312// where path is expected to have already been cleaned by naming.Join.
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700313func setPermissions(ctx *context.T, store *fs.Memstore, path string, perms access.Permissions, version string) error {
Robin Thellend4da2d8f2015-04-16 17:28:45 -0700314 if version != "" {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700315 _, oversion, err := getPermissions(ctx, store, path)
Robin Thellend4da2d8f2015-04-16 17:28:45 -0700316 if verror.ErrorID(err) == verror.ErrNoExist.ID {
317 oversion = version
318 } else if err != nil {
319 return err
320 }
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800321
Robin Thellend4da2d8f2015-04-16 17:28:45 -0700322 if oversion != version {
323 return verror.NewErrBadVersion(nil)
324 }
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800325 }
326
327 tname, err := store.BindTransactionRoot("").CreateTransaction(nil)
328 if err != nil {
329 return err
330 }
331
332 object := store.BindObject(path)
333
Adam Sadovskya4d4a692015-04-20 11:36:49 -0700334 if _, err := object.Put(nil, perms); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800335 return err
336 }
337 if err := store.BindTransaction(tname).Commit(nil); err != nil {
Todd Wangff73e1f2015-02-10 21:45:52 -0800338 return verror.New(ErrOperationFailed, nil)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800339 }
340 return nil
341}
Robert Kroeger3cd5ed52015-06-23 14:44:47 -0700342
Robert Kroeger78947322015-06-24 14:12:17 -0700343func (i *appRepoService) tidyRemoveVersions(call rpc.ServerCall, tname, appName, profile string, versions []string) error {
344 for _, v := range versions {
345 path := naming.Join(tname, "/applications", appName, profile, v)
346 object := i.store.BindObject(path)
347 if err := object.Remove(call); err != nil {
348 return err
349 }
350 }
351 return nil
352}
353
354// numberOfVersionsToKeep can be set for tests.
355var numberOfVersionsToKeep = 5
356
Robert Kroeger3cd5ed52015-06-23 14:44:47 -0700357func (i *appRepoService) TidyNow(ctx *context.T, call rpc.ServerCall) error {
Robert Kroeger78947322015-06-24 14:12:17 -0700358 ctx.VI(2).Infof("%v.TidyNow()", i.suffix)
359 i.store.Lock()
360 defer i.store.Unlock()
361
362 tname, err := i.store.BindTransactionRoot("").CreateTransaction(call)
363 if err != nil {
364 return err
365 }
366
367 apps, err := i.allApplications()
368 if err != nil {
369 return err
370 }
371
372 for _, app := range apps {
373 profiles, err := i.store.BindObject(naming.Join("/applications", app)).Children()
374 if err != nil {
375 return err
376 }
377
378 for _, profile := range profiles {
379 versions, err := i.store.BindObject(naming.Join("/applications", app, profile)).Children()
380 if err != nil {
381 return err
382 }
383
384 lv := len(versions)
385 if lv <= numberOfVersionsToKeep {
386 continue
387 }
388
389 // Per assumption in Match, version names should ascend.
390 sort.Strings(versions)
391 versionsToRemove := versions[0 : lv-numberOfVersionsToKeep]
392 if err := i.tidyRemoveVersions(call, tname, app, profile, versionsToRemove); err != nil {
393 return err
394 }
395 }
396 }
397
398 if err := i.store.BindTransaction(tname).Commit(call); err != nil {
399 return verror.New(ErrOperationFailed, ctx)
400 }
401 return nil
402
Robert Kroeger3cd5ed52015-06-23 14:44:47 -0700403}