Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(784)

Side by Side Diff: go/src/infra/tools/cipd/client.go

Issue 1381583007: cipd: Implement local cache for ResolveVersion(...) call with tags. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@cipd-init
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /* 5 /*
6 Package cipd implements client side of Chrome Infra Package Deployer. 6 Package cipd implements client side of Chrome Infra Package Deployer.
7 7
8 TODO: write more. 8 TODO: write more.
9 9
10 Binary package file format (in free form representation): 10 Binary package file format (in free form representation):
(...skipping 23 matching lines...) Expand all
34 */ 34 */
35 package cipd 35 package cipd
36 36
37 import ( 37 import (
38 "bufio" 38 "bufio"
39 "errors" 39 "errors"
40 "fmt" 40 "fmt"
41 "io" 41 "io"
42 "net/http" 42 "net/http"
43 "os" 43 "os"
44 "path/filepath"
44 "sort" 45 "sort"
45 "strings" 46 "strings"
47 "sync"
46 "time" 48 "time"
47 49
48 "github.com/luci/luci-go/common/logging" 50 "github.com/luci/luci-go/common/logging"
49 51
50 "infra/tools/cipd/common" 52 "infra/tools/cipd/common"
53 "infra/tools/cipd/internal"
51 "infra/tools/cipd/local" 54 "infra/tools/cipd/local"
52 ) 55 )
53 56
54 // PackageACLChangeAction defines a flavor of PackageACLChange. 57 // PackageACLChangeAction defines a flavor of PackageACLChange.
55 type PackageACLChangeAction string 58 type PackageACLChangeAction string
56 59
57 const ( 60 const (
58 // GrantRole is used in PackageACLChange to request a role to be granted . 61 // GrantRole is used in PackageACLChange to request a role to be granted .
59 GrantRole PackageACLChangeAction = "GRANT" 62 GrantRole PackageACLChangeAction = "GRANT"
60 // RevokeRole is used in PackageACLChange to request a role to be revoke d. 63 // RevokeRole is used in PackageACLChange to request a role to be revoke d.
61 RevokeRole PackageACLChangeAction = "REVOKE" 64 RevokeRole PackageACLChangeAction = "REVOKE"
62 65
63 // CASFinalizationTimeout is how long to wait for CAS service to finaliz e the upload. 66 // CASFinalizationTimeout is how long to wait for CAS service to finaliz e the upload.
64 CASFinalizationTimeout = 1 * time.Minute 67 CASFinalizationTimeout = 1 * time.Minute
65 // SetRefTimeout is how long to wait for an instance to be processed whe n setting a ref. 68 // SetRefTimeout is how long to wait for an instance to be processed whe n setting a ref.
66 SetRefTimeout = 1 * time.Minute 69 SetRefTimeout = 1 * time.Minute
67 // TagAttachTimeout is how long to wait for an instance to be processed when attaching tags. 70 // TagAttachTimeout is how long to wait for an instance to be processed when attaching tags.
68 TagAttachTimeout = 1 * time.Minute 71 TagAttachTimeout = 1 * time.Minute
69 72
70 // UserAgent is HTTP user agent string for CIPD client. 73 // UserAgent is HTTP user agent string for CIPD client.
71 UserAgent = "cipd 1.0" 74 UserAgent = "cipd 1.0"
72 75
73 // ServiceURL is URL of a backend to connect to by default. 76 // ServiceURL is URL of a backend to connect to by default.
74 ServiceURL = "https://chrome-infra-packages.appspot.com" 77 ServiceURL = "https://chrome-infra-packages.appspot.com"
75 ) 78 )
76 79
77 var ( 80 var (
78 // ErrFinalizationTimeout is returned if CAS service can not finalize up load fast enough. 81 // ErrFinalizationTimeout is returned if CAS service can not finalize up load fast enough.
79 » ErrFinalizationTimeout = errors.New("Timeout while waiting for CAS servi ce to finalize the upload") 82 » ErrFinalizationTimeout = errors.New("timeout while waiting for CAS servi ce to finalize the upload")
80 // ErrBadUpload is returned when a package file is uploaded, but servers asks us to upload it again. 83 // ErrBadUpload is returned when a package file is uploaded, but servers asks us to upload it again.
81 » ErrBadUpload = errors.New("Package file is uploaded, but servers asks us to upload it again") 84 » ErrBadUpload = errors.New("package file is uploaded, but servers asks us to upload it again")
82 // ErrBadUploadSession is returned by UploadToCAS if provided UploadSess ion is not valid. 85 // ErrBadUploadSession is returned by UploadToCAS if provided UploadSess ion is not valid.
83 » ErrBadUploadSession = errors.New("UploadURL must be set if UploadSession ID is used") 86 » ErrBadUploadSession = errors.New("uploadURL must be set if UploadSession ID is used")
84 // ErrUploadSessionDied is returned by UploadToCAS if upload session sud denly disappeared. 87 // ErrUploadSessionDied is returned by UploadToCAS if upload session sud denly disappeared.
85 » ErrUploadSessionDied = errors.New("Upload session is unexpectedly missin g") 88 » ErrUploadSessionDied = errors.New("upload session is unexpectedly missin g")
86 // ErrNoUploadSessionID is returned by UploadToCAS if server didn't prov ide upload session ID. 89 // ErrNoUploadSessionID is returned by UploadToCAS if server didn't prov ide upload session ID.
87 » ErrNoUploadSessionID = errors.New("Server didn't provide upload session ID") 90 » ErrNoUploadSessionID = errors.New("server didn't provide upload session ID")
88 // ErrSetRefTimeout is returned when service refuses to move a ref for a long time. 91 // ErrSetRefTimeout is returned when service refuses to move a ref for a long time.
89 » ErrSetRefTimeout = errors.New("Timeout while moving a ref") 92 » ErrSetRefTimeout = errors.New("timeout while moving a ref")
90 // ErrAttachTagsTimeout is returned when service refuses to accept tags for a long time. 93 // ErrAttachTagsTimeout is returned when service refuses to accept tags for a long time.
91 » ErrAttachTagsTimeout = errors.New("Timeout while attaching tags") 94 » ErrAttachTagsTimeout = errors.New("timeout while attaching tags")
92 // ErrDownloadError is returned by FetchInstance on download errors. 95 // ErrDownloadError is returned by FetchInstance on download errors.
93 » ErrDownloadError = errors.New("Failed to download the package file after multiple attempts") 96 » ErrDownloadError = errors.New("failed to download the package file after multiple attempts")
94 // ErrUploadError is returned by RegisterInstance and UploadToCAS on upl oad errors. 97 // ErrUploadError is returned by RegisterInstance and UploadToCAS on upl oad errors.
95 » ErrUploadError = errors.New("Failed to upload the package file after mul tiple attempts") 98 » ErrUploadError = errors.New("failed to upload the package file after mul tiple attempts")
96 // ErrAccessDenined is returned by calls talking to backend on 401 or 40 3 HTTP errors. 99 // ErrAccessDenined is returned by calls talking to backend on 401 or 40 3 HTTP errors.
97 » ErrAccessDenined = errors.New("Access denied (not authenticated or not e nough permissions)") 100 » ErrAccessDenined = errors.New("access denied (not authenticated or not e nough permissions)")
98 // ErrBackendInaccessible is returned by calls talking to backed if it d oesn't response. 101 // ErrBackendInaccessible is returned by calls talking to backed if it d oesn't response.
99 » ErrBackendInaccessible = errors.New("Request to the backend failed after multiple attempts") 102 » ErrBackendInaccessible = errors.New("request to the backend failed after multiple attempts")
100 // ErrEnsurePackagesFailed is returned by EnsurePackages if something is not right. 103 // ErrEnsurePackagesFailed is returned by EnsurePackages if something is not right.
101 » ErrEnsurePackagesFailed = errors.New("Failed to update packages, see the log") 104 » ErrEnsurePackagesFailed = errors.New("failed to update packages, see the log")
102 ) 105 )
103 106
104 // PackageACL is per package path per role access control list that is a part of 107 // PackageACL is per package path per role access control list that is a part of
105 // larger overall ACL: ACL for package "a/b/c" is a union of PackageACLs for "a" 108 // larger overall ACL: ACL for package "a/b/c" is a union of PackageACLs for "a"
106 // "a/b" and "a/b/c". 109 // "a/b" and "a/b/c".
107 type PackageACL struct { 110 type PackageACL struct {
108 // PackagePath is a package subpath this ACL is defined for. 111 // PackagePath is a package subpath this ACL is defined for.
109 PackagePath string 112 PackagePath string
110 // Role is a role that listed users have, e.g. 'READER', 'WRITER', ... 113 // Role is a role that listed users have, e.g. 'READER', 'WRITER', ...
111 Role string 114 Role string
(...skipping 16 matching lines...) Expand all
128 } 131 }
129 132
130 // UploadSession describes open CAS upload session. 133 // UploadSession describes open CAS upload session.
131 type UploadSession struct { 134 type UploadSession struct {
132 // ID identifies upload session in the backend. 135 // ID identifies upload session in the backend.
133 ID string 136 ID string
134 // URL is where to upload the data to. 137 // URL is where to upload the data to.
135 URL string 138 URL string
136 } 139 }
137 140
138 // Client provides high-level CIPD client interface. 141 // Client provides high-level CIPD client interface. Thread safe.
139 type Client interface { 142 type Client interface {
140 // FetchACL returns a list of PackageACL objects (parent paths first) th at 143 // FetchACL returns a list of PackageACL objects (parent paths first) th at
141 // together define the access control list for the given package subpath . 144 // together define the access control list for the given package subpath .
142 FetchACL(packagePath string) ([]PackageACL, error) 145 FetchACL(packagePath string) ([]PackageACL, error)
143 146
144 // ModifyACL applies a set of PackageACLChanges to a package path. 147 // ModifyACL applies a set of PackageACLChanges to a package path.
145 ModifyACL(packagePath string, changes []PackageACLChange) error 148 ModifyACL(packagePath string, changes []PackageACLChange) error
146 149
147 // UploadToCAS uploads package data blob to Content Addressed Store if i t is 150 // UploadToCAS uploads package data blob to Content Addressed Store if i t is
148 // not there already. The data is addressed by SHA1 hash (also known as 151 // not there already. The data is addressed by SHA1 hash (also known as
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 // <package name> <desired version>. Whitespaces are ignored. Lines that start 187 // <package name> <desired version>. Whitespaces are ignored. Lines that start
185 // with '#' are ignored. Version can be specified as instance ID, tag or ref. 188 // with '#' are ignored. Version can be specified as instance ID, tag or ref.
186 // Will resolve tags and refs to concrete instance IDs by calling the ba ckend. 189 // Will resolve tags and refs to concrete instance IDs by calling the ba ckend.
187 ProcessEnsureFile(r io.Reader) ([]common.Pin, error) 190 ProcessEnsureFile(r io.Reader) ([]common.Pin, error)
188 191
189 // EnsurePackages is high-level interface for installation, removal and update 192 // EnsurePackages is high-level interface for installation, removal and update
190 // of packages inside the installation site root. Given a description of 193 // of packages inside the installation site root. Given a description of
191 // what packages (and versions) should be installed it will do all neces sary 194 // what packages (and versions) should be installed it will do all neces sary
192 // actions to bring the state of the site root to the desired one. 195 // actions to bring the state of the site root to the desired one.
193 EnsurePackages(pins []common.Pin) error 196 EnsurePackages(pins []common.Pin) error
197
198 // Close should be called to dump any cached state to disk.
199 Close()
194 } 200 }
195 201
196 // HTTPClientFactory lazily creates http.Client to use for making requests. 202 // HTTPClientFactory lazily creates http.Client to use for making requests.
197 type HTTPClientFactory func() (*http.Client, error) 203 type HTTPClientFactory func() (*http.Client, error)
198 204
199 // ClientOptions is passed to NewClient factory function. 205 // ClientOptions is passed to NewClient factory function.
200 type ClientOptions struct { 206 type ClientOptions struct {
201 // ServiceURL is root URL of the backend service. 207 // ServiceURL is root URL of the backend service.
202 ServiceURL string 208 ServiceURL string
203 » // Root is a site root directory (where packages will be installed). It can 209
204 » // be empty string if client is not going to be used to deploy or remove local packages. 210 » // Root is a site root directory (a directory where packages will be
211 » // installed to). It also hosts .cipd/* directory that tracks internal s tate
212 » // of installed packages and keeps various cache files. 'Root' can be an empty
213 » // string if the client is not going to be used to deploy or remove loca l
214 » // packages. In that case caches are also disabled.
205 Root string 215 Root string
216
206 // Logger is a logger to use for logs (null-logger by default). 217 // Logger is a logger to use for logs (null-logger by default).
207 Logger logging.Logger 218 Logger logging.Logger
208 » // AuthenticatedClientFactory lazily creates http.Client to use for maki ng RPC requests. 219
220 » // AuthenticatedClientFactory lazily creates http.Client to use for maki ng
221 » // RPC requests.
209 AuthenticatedClientFactory HTTPClientFactory 222 AuthenticatedClientFactory HTTPClientFactory
210 » // AnonymousClientFactory lazily creates http.Client to use for making r equests to storage. 223
224 » // AnonymousClientFactory lazily creates http.Client to use for making
225 » // requests to storage.
211 AnonymousClientFactory HTTPClientFactory 226 AnonymousClientFactory HTTPClientFactory
227
212 // UserAgent is put into User-Agent HTTP header with each request. 228 // UserAgent is put into User-Agent HTTP header with each request.
213 UserAgent string 229 UserAgent string
214 } 230 }
215 231
216 // NewClient initializes CIPD client object. 232 // NewClient initializes CIPD client object.
217 func NewClient(opts ClientOptions) Client { 233 func NewClient(opts ClientOptions) Client {
218 if opts.ServiceURL == "" { 234 if opts.ServiceURL == "" {
219 opts.ServiceURL = ServiceURL 235 opts.ServiceURL = ServiceURL
220 } 236 }
221 if opts.Logger == nil { 237 if opts.Logger == nil {
(...skipping 14 matching lines...) Expand all
236 } 252 }
237 c.remote = &remoteImpl{c} 253 c.remote = &remoteImpl{c}
238 c.storage = &storageImpl{c, uploadChunkSize} 254 c.storage = &storageImpl{c, uploadChunkSize}
239 c.deployer = local.NewDeployer(opts.Root, opts.Logger) 255 c.deployer = local.NewDeployer(opts.Root, opts.Logger)
240 return c 256 return c
241 } 257 }
242 258
243 type clientImpl struct { 259 type clientImpl struct {
244 ClientOptions 260 ClientOptions
245 261
246 » // clock provides current time and ability to sleep. 262 » // lock protects lazily initialized portions of the client.
263 » lock sync.Mutex
264
265 » // clock provides current time and ability to sleep. Thread safe.
247 clock clock 266 clock clock
248 » // remote knows how to call backend REST API. 267
268 » // remote knows how to call backend REST API. Thread safe.
249 remote remote 269 remote remote
270
250 // storage knows how to upload and download raw binaries using signed UR Ls. 271 // storage knows how to upload and download raw binaries using signed UR Ls.
272 // Thread safe.
251 storage storage 273 storage storage
252 » // deployer knows how to install packages to local file system. 274
275 » // deployer knows how to install packages to local file system. Thread s afe.
253 deployer local.Deployer 276 deployer local.Deployer
254 277
255 » // authClient is a lazily created http.Client to use for authenticated r equests. 278 » // tagCache is used to cache (pkgname, tag) -> instanceID mapping.
279 » // Thread safe, but lazily initialized under lock.
280 » tagCache internal.TagCache
281
282 » // authClient is a lazily created http.Client to use for authenticated
283 » // requests. Thread safe, but lazily initialized under lock.
256 authClient *http.Client 284 authClient *http.Client
285
257 // anonClient is a lazily created http.Client to use for anonymous reque sts. 286 // anonClient is a lazily created http.Client to use for anonymous reque sts.
287 // Thread safe, but lazily initialized under lock.
258 anonClient *http.Client 288 anonClient *http.Client
259 } 289 }
260 290
261 // doAuthenticatedHTTPRequest is used by remote implementation to make HTTP call s. 291 // doAuthenticatedHTTPRequest is used by remote implementation to make HTTP call s.
262 func (client *clientImpl) doAuthenticatedHTTPRequest(req *http.Request) (*http.R esponse, error) { 292 func (client *clientImpl) doAuthenticatedHTTPRequest(req *http.Request) (*http.R esponse, error) {
263 » if client.authClient == nil { 293 » return client.doRequest(req, &client.authClient, client.AuthenticatedCli entFactory)
264 » » var err error
265 » » client.authClient, err = client.AuthenticatedClientFactory()
266 » » if err != nil {
267 » » » return nil, err
268 » » }
269 » }
270 » return client.authClient.Do(req)
271 } 294 }
272 295
273 // doAnonymousHTTPRequest is used by storage implementation to make HTTP calls. 296 // doAnonymousHTTPRequest is used by storage implementation to make HTTP calls.
274 func (client *clientImpl) doAnonymousHTTPRequest(req *http.Request) (*http.Respo nse, error) { 297 func (client *clientImpl) doAnonymousHTTPRequest(req *http.Request) (*http.Respo nse, error) {
275 » if client.anonClient == nil { 298 » return client.doRequest(req, &client.anonClient, client.AnonymousClientF actory)
299 }
300
301 // doRequest lazy-initializes http.Client by calling giving callback and then
tandrii(chromium) 2015/09/30 18:11:15 s/by calling giving callback/using provided factor
Vadim Sh. 2015/09/30 22:50:59 Done.
302 // executes the request.
303 func (client *clientImpl) doRequest(req *http.Request, c **http.Client, fac HTTP ClientFactory) (*http.Response, error) {
304 » httpClient, err := func() (*http.Client, error) {
305 » » client.lock.Lock()
306 » » defer client.lock.Unlock()
276 var err error 307 var err error
277 » » client.anonClient, err = client.AnonymousClientFactory() 308 » » if *c == nil {
278 » » if err != nil { 309 » » » *c, err = fac()
279 » » » return nil, err 310 » » }
311 » » return *c, err
312 » }()
313 » if err != nil {
314 » » return nil, err
315 » }
316 » return httpClient.Do(req)
317 }
318
319 // tagCachePath returns path to a tag cache file or "" if no root dir.
320 func (client *clientImpl) tagCachePath() string {
321 » if client.Root == "" {
322 » » return ""
323 » }
324 » return filepath.Join(client.Root, local.SiteServiceDir, "tagcache.db")
325 }
326
327 // getTagCache lazy-initializes tagCache instance and returns it.
328 func (client *clientImpl) getTagCache() internal.TagCache {
329 » client.lock.Lock()
330 » defer client.lock.Unlock()
331 » if client.tagCache == nil {
332 » » if path := client.tagCachePath(); path != "" {
333 » » » var err error
334 » » » client.tagCache, err = internal.LoadTagCacheFromFile(pat h)
335 » » » if err != nil {
336 » » » » client.Logger.Warningf("cipd: failed to load tag cache - %s", err)
337 » » » » client.tagCache = internal.NewTagCache()
nodir 2015/09/30 19:08:23 remove this
Vadim Sh. 2015/09/30 22:50:59 Done.
338 » » » }
339 » » } else {
340 » » » client.tagCache = internal.NewTagCache()
nodir 2015/09/30 19:08:23 remove this
Vadim Sh. 2015/09/30 22:50:58 Done.
280 } 341 }
281 } 342 }
nodir 2015/09/30 19:08:23 add if client.tagCache == nil { client.TagCach
Vadim Sh. 2015/09/30 22:50:58 Done.
282 » return client.anonClient.Do(req) 343 » return client.tagCache
344 }
345
346 // closeTagCache dumps any changes made to tag cache to disk, if necessary.
347 // Called under lock.
tandrii(chromium) 2015/09/30 18:11:15 nit suggestion: s/.../Must be called under lock.
Vadim Sh. 2015/09/30 22:50:59 Done. Whatever.
348 func (client *clientImpl) closeTagCache() {
349 » path := client.tagCachePath()
350 » if client.tagCache == nil || path == "" || !client.tagCache.Dirty() {
351 » » client.tagCache = nil
352 » » return
353 » }
354 » //It's tiny in size (and protobuf can't serialize to io.Reader anyway). Then
tandrii(chromium) 2015/09/30 18:11:15 nit: space between // and It nit: s/Then/So,
Vadim Sh. 2015/09/30 22:50:59 Done.
355 » // dump it to disk via FileSystem object to deal with possible concurren t
356 » // updates, missing directories, etc.
357 » fs := local.NewFileSystem(filepath.Dir(path), client.Logger)
358 » out, err := client.tagCache.Save()
359 » if err == nil {
360 » » err = fs.EnsureFile(path, out, 0666)
361 » }
362 » if err != nil {
363 » » client.Logger.Warningf("cipd: failed to update tag cache - %s", err)
364 » }
365 » client.tagCache = nil
283 } 366 }
284 367
285 func (client *clientImpl) FetchACL(packagePath string) ([]PackageACL, error) { 368 func (client *clientImpl) FetchACL(packagePath string) ([]PackageACL, error) {
286 return client.remote.fetchACL(packagePath) 369 return client.remote.fetchACL(packagePath)
287 } 370 }
288 371
289 func (client *clientImpl) ModifyACL(packagePath string, changes []PackageACLChan ge) error { 372 func (client *clientImpl) ModifyACL(packagePath string, changes []PackageACLChan ge) error {
290 return client.remote.modifyACL(packagePath, changes) 373 return client.remote.modifyACL(packagePath, changes)
291 } 374 }
292 375
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 if err := common.ValidatePackageName(packageName); err != nil { 444 if err := common.ValidatePackageName(packageName); err != nil {
362 return common.Pin{}, err 445 return common.Pin{}, err
363 } 446 }
364 // Is it instance ID already? Don't bother calling the backend. 447 // Is it instance ID already? Don't bother calling the backend.
365 if common.ValidateInstanceID(version) == nil { 448 if common.ValidateInstanceID(version) == nil {
366 return common.Pin{PackageName: packageName, InstanceID: version} , nil 449 return common.Pin{PackageName: packageName, InstanceID: version} , nil
367 } 450 }
368 if err := common.ValidateInstanceVersion(version); err != nil { 451 if err := common.ValidateInstanceVersion(version); err != nil {
369 return common.Pin{}, err 452 return common.Pin{}, err
370 } 453 }
371 » return client.remote.resolveVersion(packageName, version) 454 » // Use local cache when resolving tags to avoid round trips to backed wh en
tandrii(chromium) 2015/09/30 18:11:15 s/backed/backend
Vadim Sh. 2015/09/30 22:50:59 Done.
455 » // calling same 'cipd ensure' command again and again.
456 » isTag := common.ValidateInstanceTag(version) == nil
457 » if isTag {
458 » » cached := client.getTagCache().ResolveTag(packageName, version)
459 » » if cached.InstanceID != "" {
460 » » » return cached, nil
461 » » }
462 » }
463 » pin, err := client.remote.resolveVersion(packageName, version)
464 » if err != nil {
465 » » return pin, err
466 » }
467 » if isTag {
468 » » client.getTagCache().AddTag(pin, version)
469 » }
470 » return pin, nil
372 } 471 }
373 472
374 func (client *clientImpl) RegisterInstance(instance local.PackageInstance) error { 473 func (client *clientImpl) RegisterInstance(instance local.PackageInstance) error {
375 // Attempt to register. 474 // Attempt to register.
376 client.Logger.Infof("cipd: registering %s", instance.Pin()) 475 client.Logger.Infof("cipd: registering %s", instance.Pin())
377 result, err := client.remote.registerInstance(instance.Pin()) 476 result, err := client.remote.registerInstance(instance.Pin())
378 if err != nil { 477 if err != nil {
379 return err 478 return err
380 } 479 }
381 480
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
622 } 721 }
623 } 722 }
624 723
625 if !fail { 724 if !fail {
626 client.Logger.Infof("All changes applied.") 725 client.Logger.Infof("All changes applied.")
627 return nil 726 return nil
628 } 727 }
629 return ErrEnsurePackagesFailed 728 return ErrEnsurePackagesFailed
630 } 729 }
631 730
731 func (client *clientImpl) Close() {
732 client.lock.Lock()
733 defer client.lock.Unlock()
734 client.closeTagCache()
735 client.authClient = nil
736 client.anonClient = nil
737 }
738
632 //////////////////////////////////////////////////////////////////////////////// 739 ////////////////////////////////////////////////////////////////////////////////
633 // Private structs and interfaces. 740 // Private structs and interfaces.
634 741
635 type clock interface { 742 type clock interface {
636 now() time.Time 743 now() time.Time
637 sleep(time.Duration) 744 sleep(time.Duration)
638 } 745 }
639 746
640 type remote interface { 747 type remote interface {
641 fetchACL(packagePath string) ([]PackageACL, error) 748 fetchACL(packagePath string) ([]PackageACL, error)
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
701 } 808 }
702 809
703 // buildInstanceIDMap builds mapping {package name -> instance ID}. 810 // buildInstanceIDMap builds mapping {package name -> instance ID}.
704 func buildInstanceIDMap(pins []common.Pin) map[string]string { 811 func buildInstanceIDMap(pins []common.Pin) map[string]string {
705 out := map[string]string{} 812 out := map[string]string{}
706 for _, p := range pins { 813 for _, p := range pins {
707 out[p.PackageName] = p.InstanceID 814 out[p.PackageName] = p.InstanceID
708 } 815 }
709 return out 816 return out
710 } 817 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698