| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 // <package name> <desired version>. Whitespaces are ignored. Lines that
start | 191 // <package name> <desired version>. Whitespaces are ignored. Lines that
start |
| 189 // with '#' are ignored. Version can be specified as instance ID, tag or
ref. | 192 // with '#' are ignored. Version can be specified as instance ID, tag or
ref. |
| 190 // Will resolve tags and refs to concrete instance IDs by calling the ba
ckend. | 193 // Will resolve tags and refs to concrete instance IDs by calling the ba
ckend. |
| 191 ProcessEnsureFile(r io.Reader) ([]common.Pin, error) | 194 ProcessEnsureFile(r io.Reader) ([]common.Pin, error) |
| 192 | 195 |
| 193 // EnsurePackages is high-level interface for installation, removal and
update | 196 // EnsurePackages is high-level interface for installation, removal and
update |
| 194 // of packages inside the installation site root. Given a description of | 197 // of packages inside the installation site root. Given a description of |
| 195 // what packages (and versions) should be installed it will do all neces
sary | 198 // what packages (and versions) should be installed it will do all neces
sary |
| 196 // actions to bring the state of the site root to the desired one. | 199 // actions to bring the state of the site root to the desired one. |
| 197 EnsurePackages(pins []common.Pin) error | 200 EnsurePackages(pins []common.Pin) error |
| 201 |
| 202 // Close should be called to dump any cached state to disk. |
| 203 Close() |
| 198 } | 204 } |
| 199 | 205 |
| 200 // HTTPClientFactory lazily creates http.Client to use for making requests. | 206 // HTTPClientFactory lazily creates http.Client to use for making requests. |
| 201 type HTTPClientFactory func() (*http.Client, error) | 207 type HTTPClientFactory func() (*http.Client, error) |
| 202 | 208 |
| 203 // ClientOptions is passed to NewClient factory function. | 209 // ClientOptions is passed to NewClient factory function. |
| 204 type ClientOptions struct { | 210 type ClientOptions struct { |
| 205 // ServiceURL is root URL of the backend service. | 211 // ServiceURL is root URL of the backend service. |
| 206 ServiceURL string | 212 ServiceURL string |
| 207 » // Root is a site root directory (where packages will be installed). It
can | 213 |
| 208 » // be empty string if client is not going to be used to deploy or remove
local packages. | 214 » // Root is a site root directory (a directory where packages will be |
| 215 » // installed to). It also hosts .cipd/* directory that tracks internal s
tate |
| 216 » // of installed packages and keeps various cache files. 'Root' can be an
empty |
| 217 » // string if the client is not going to be used to deploy or remove loca
l |
| 218 » // packages. In that case caches are also disabled. |
| 209 Root string | 219 Root string |
| 220 |
| 210 // Logger is a logger to use for logs (null-logger by default). | 221 // Logger is a logger to use for logs (null-logger by default). |
| 211 Logger logging.Logger | 222 Logger logging.Logger |
| 212 » // AuthenticatedClientFactory lazily creates http.Client to use for maki
ng RPC requests. | 223 |
| 224 » // AuthenticatedClientFactory lazily creates http.Client to use for maki
ng |
| 225 » // RPC requests. |
| 213 AuthenticatedClientFactory HTTPClientFactory | 226 AuthenticatedClientFactory HTTPClientFactory |
| 214 » // AnonymousClientFactory lazily creates http.Client to use for making r
equests to storage. | 227 |
| 228 » // AnonymousClientFactory lazily creates http.Client to use for making |
| 229 » // requests to storage. |
| 215 AnonymousClientFactory HTTPClientFactory | 230 AnonymousClientFactory HTTPClientFactory |
| 231 |
| 216 // UserAgent is put into User-Agent HTTP header with each request. | 232 // UserAgent is put into User-Agent HTTP header with each request. |
| 217 UserAgent string | 233 UserAgent string |
| 218 } | 234 } |
| 219 | 235 |
| 220 // NewClient initializes CIPD client object. | 236 // NewClient initializes CIPD client object. |
| 221 func NewClient(opts ClientOptions) Client { | 237 func NewClient(opts ClientOptions) Client { |
| 222 if opts.ServiceURL == "" { | 238 if opts.ServiceURL == "" { |
| 223 opts.ServiceURL = ServiceURL | 239 opts.ServiceURL = ServiceURL |
| 224 } | 240 } |
| 225 if opts.Logger == nil { | 241 if opts.Logger == nil { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 240 } | 256 } |
| 241 c.remote = &remoteImpl{c} | 257 c.remote = &remoteImpl{c} |
| 242 c.storage = &storageImpl{c, uploadChunkSize} | 258 c.storage = &storageImpl{c, uploadChunkSize} |
| 243 c.deployer = local.NewDeployer(opts.Root, opts.Logger) | 259 c.deployer = local.NewDeployer(opts.Root, opts.Logger) |
| 244 return c | 260 return c |
| 245 } | 261 } |
| 246 | 262 |
| 247 type clientImpl struct { | 263 type clientImpl struct { |
| 248 ClientOptions | 264 ClientOptions |
| 249 | 265 |
| 250 » // clock provides current time and ability to sleep. | 266 » // lock protects lazily initialized portions of the client. |
| 267 » lock sync.Mutex |
| 268 |
| 269 » // clock provides current time and ability to sleep. Thread safe. |
| 251 clock clock | 270 clock clock |
| 252 » // remote knows how to call backend REST API. | 271 |
| 272 » // remote knows how to call backend REST API. Thread safe. |
| 253 remote remote | 273 remote remote |
| 274 |
| 254 // storage knows how to upload and download raw binaries using signed UR
Ls. | 275 // storage knows how to upload and download raw binaries using signed UR
Ls. |
| 276 // Thread safe. |
| 255 storage storage | 277 storage storage |
| 256 » // deployer knows how to install packages to local file system. | 278 |
| 279 » // deployer knows how to install packages to local file system. Thread s
afe. |
| 257 deployer local.Deployer | 280 deployer local.Deployer |
| 258 | 281 |
| 259 » // authClient is a lazily created http.Client to use for authenticated r
equests. | 282 » // tagCache is used to cache (pkgname, tag) -> instanceID mapping. |
| 283 » // Thread safe, but lazily initialized under lock. |
| 284 » tagCache *internal.TagCache |
| 285 |
| 286 » // authClient is a lazily created http.Client to use for authenticated |
| 287 » // requests. Thread safe, but lazily initialized under lock. |
| 260 authClient *http.Client | 288 authClient *http.Client |
| 289 |
| 261 // anonClient is a lazily created http.Client to use for anonymous reque
sts. | 290 // anonClient is a lazily created http.Client to use for anonymous reque
sts. |
| 291 // Thread safe, but lazily initialized under lock. |
| 262 anonClient *http.Client | 292 anonClient *http.Client |
| 263 } | 293 } |
| 264 | 294 |
| 265 // doAuthenticatedHTTPRequest is used by remote implementation to make HTTP call
s. | 295 // doAuthenticatedHTTPRequest is used by remote implementation to make HTTP call
s. |
| 266 func (client *clientImpl) doAuthenticatedHTTPRequest(req *http.Request) (*http.R
esponse, error) { | 296 func (client *clientImpl) doAuthenticatedHTTPRequest(req *http.Request) (*http.R
esponse, error) { |
| 267 » if client.authClient == nil { | 297 » return client.doRequest(req, &client.authClient, client.AuthenticatedCli
entFactory) |
| 268 » » var err error | |
| 269 » » client.authClient, err = client.AuthenticatedClientFactory() | |
| 270 » » if err != nil { | |
| 271 » » » return nil, err | |
| 272 » » } | |
| 273 » } | |
| 274 » return client.authClient.Do(req) | |
| 275 } | 298 } |
| 276 | 299 |
| 277 // doAnonymousHTTPRequest is used by storage implementation to make HTTP calls. | 300 // doAnonymousHTTPRequest is used by storage implementation to make HTTP calls. |
| 278 func (client *clientImpl) doAnonymousHTTPRequest(req *http.Request) (*http.Respo
nse, error) { | 301 func (client *clientImpl) doAnonymousHTTPRequest(req *http.Request) (*http.Respo
nse, error) { |
| 279 » if client.anonClient == nil { | 302 » return client.doRequest(req, &client.anonClient, client.AnonymousClientF
actory) |
| 303 } |
| 304 |
| 305 // doRequest lazy-initializes http.Client using provided factory and then |
| 306 // executes the request. |
| 307 func (client *clientImpl) doRequest(req *http.Request, c **http.Client, fac HTTP
ClientFactory) (*http.Response, error) { |
| 308 » httpClient, err := func() (*http.Client, error) { |
| 309 » » client.lock.Lock() |
| 310 » » defer client.lock.Unlock() |
| 280 var err error | 311 var err error |
| 281 » » client.anonClient, err = client.AnonymousClientFactory() | 312 » » if *c == nil { |
| 282 » » if err != nil { | 313 » » » *c, err = fac() |
| 283 » » » return nil, err | 314 » » } |
| 315 » » return *c, err |
| 316 » }() |
| 317 » if err != nil { |
| 318 » » return nil, err |
| 319 » } |
| 320 » return httpClient.Do(req) |
| 321 } |
| 322 |
| 323 // tagCachePath returns path to a tag cache file or "" if no root dir. |
| 324 func (client *clientImpl) tagCachePath() string { |
| 325 » if client.Root == "" { |
| 326 » » return "" |
| 327 » } |
| 328 » return filepath.Join(client.Root, local.SiteServiceDir, "tagcache.db") |
| 329 } |
| 330 |
| 331 // getTagCache lazy-initializes tagCache instance and returns it. |
| 332 func (client *clientImpl) getTagCache() *internal.TagCache { |
| 333 » client.lock.Lock() |
| 334 » defer client.lock.Unlock() |
| 335 » if client.tagCache == nil { |
| 336 » » if path := client.tagCachePath(); path != "" { |
| 337 » » » var err error |
| 338 » » » client.tagCache, err = internal.LoadTagCacheFromFile(pat
h) |
| 339 » » » if err != nil { |
| 340 » » » » client.Logger.Warningf("cipd: failed to load tag
cache - %s", err) |
| 341 » » » } |
| 342 » » } |
| 343 » » if client.tagCache == nil { |
| 344 » » » client.tagCache = &internal.TagCache{} |
| 284 } | 345 } |
| 285 } | 346 } |
| 286 » return client.anonClient.Do(req) | 347 » return client.tagCache |
| 348 } |
| 349 |
| 350 // closeTagCache dumps any changes made to tag cache to disk, if necessary. |
| 351 // Must be called under lock. |
| 352 func (client *clientImpl) closeTagCache() { |
| 353 » path := client.tagCachePath() |
| 354 » if client.tagCache == nil || path == "" || !client.tagCache.Dirty() { |
| 355 » » client.tagCache = nil |
| 356 » » return |
| 357 » } |
| 358 » // It's tiny in size (and protobuf can't serialize to io.Reader anyway).
Dump |
| 359 » // it to disk via FileSystem object to deal with possible concurrent upd
ates, |
| 360 » // missing directories, etc. |
| 361 » fs := local.NewFileSystem(filepath.Dir(path), client.Logger) |
| 362 » out, err := client.tagCache.Save() |
| 363 » if err == nil { |
| 364 » » err = fs.EnsureFile(path, out, 0666) |
| 365 » } |
| 366 » if err != nil { |
| 367 » » client.Logger.Warningf("cipd: failed to update tag cache - %s",
err) |
| 368 » } |
| 369 » client.tagCache = nil |
| 287 } | 370 } |
| 288 | 371 |
| 289 func (client *clientImpl) FetchACL(packagePath string) ([]PackageACL, error) { | 372 func (client *clientImpl) FetchACL(packagePath string) ([]PackageACL, error) { |
| 290 return client.remote.fetchACL(packagePath) | 373 return client.remote.fetchACL(packagePath) |
| 291 } | 374 } |
| 292 | 375 |
| 293 func (client *clientImpl) ModifyACL(packagePath string, changes []PackageACLChan
ge) error { | 376 func (client *clientImpl) ModifyACL(packagePath string, changes []PackageACLChan
ge) error { |
| 294 return client.remote.modifyACL(packagePath, changes) | 377 return client.remote.modifyACL(packagePath, changes) |
| 295 } | 378 } |
| 296 | 379 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 365 if err := common.ValidatePackageName(packageName); err != nil { | 448 if err := common.ValidatePackageName(packageName); err != nil { |
| 366 return common.Pin{}, err | 449 return common.Pin{}, err |
| 367 } | 450 } |
| 368 // Is it instance ID already? Don't bother calling the backend. | 451 // Is it instance ID already? Don't bother calling the backend. |
| 369 if common.ValidateInstanceID(version) == nil { | 452 if common.ValidateInstanceID(version) == nil { |
| 370 return common.Pin{PackageName: packageName, InstanceID: version}
, nil | 453 return common.Pin{PackageName: packageName, InstanceID: version}
, nil |
| 371 } | 454 } |
| 372 if err := common.ValidateInstanceVersion(version); err != nil { | 455 if err := common.ValidateInstanceVersion(version); err != nil { |
| 373 return common.Pin{}, err | 456 return common.Pin{}, err |
| 374 } | 457 } |
| 375 » return client.remote.resolveVersion(packageName, version) | 458 » // Use local cache when resolving tags to avoid round trips to backend w
hen |
| 459 » // calling same 'cipd ensure' command again and again. |
| 460 » isTag := common.ValidateInstanceTag(version) == nil |
| 461 » if isTag { |
| 462 » » cached := client.getTagCache().ResolveTag(packageName, version) |
| 463 » » if cached.InstanceID != "" { |
| 464 » » » return cached, nil |
| 465 » » } |
| 466 » } |
| 467 » pin, err := client.remote.resolveVersion(packageName, version) |
| 468 » if err != nil { |
| 469 » » return pin, err |
| 470 » } |
| 471 » if isTag { |
| 472 » » client.getTagCache().AddTag(pin, version) |
| 473 » } |
| 474 » return pin, nil |
| 376 } | 475 } |
| 377 | 476 |
| 378 func (client *clientImpl) RegisterInstance(instance local.PackageInstance) error
{ | 477 func (client *clientImpl) RegisterInstance(instance local.PackageInstance) error
{ |
| 379 // Attempt to register. | 478 // Attempt to register. |
| 380 client.Logger.Infof("cipd: registering %s", instance.Pin()) | 479 client.Logger.Infof("cipd: registering %s", instance.Pin()) |
| 381 result, err := client.remote.registerInstance(instance.Pin()) | 480 result, err := client.remote.registerInstance(instance.Pin()) |
| 382 if err != nil { | 481 if err != nil { |
| 383 return err | 482 return err |
| 384 } | 483 } |
| 385 | 484 |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 636 } | 735 } |
| 637 } | 736 } |
| 638 | 737 |
| 639 if !fail { | 738 if !fail { |
| 640 client.Logger.Infof("All changes applied.") | 739 client.Logger.Infof("All changes applied.") |
| 641 return nil | 740 return nil |
| 642 } | 741 } |
| 643 return ErrEnsurePackagesFailed | 742 return ErrEnsurePackagesFailed |
| 644 } | 743 } |
| 645 | 744 |
| 745 func (client *clientImpl) Close() { |
| 746 client.lock.Lock() |
| 747 defer client.lock.Unlock() |
| 748 client.closeTagCache() |
| 749 client.authClient = nil |
| 750 client.anonClient = nil |
| 751 } |
| 752 |
| 646 //////////////////////////////////////////////////////////////////////////////// | 753 //////////////////////////////////////////////////////////////////////////////// |
| 647 // Private structs and interfaces. | 754 // Private structs and interfaces. |
| 648 | 755 |
| 649 type clock interface { | 756 type clock interface { |
| 650 now() time.Time | 757 now() time.Time |
| 651 sleep(time.Duration) | 758 sleep(time.Duration) |
| 652 } | 759 } |
| 653 | 760 |
| 654 type remote interface { | 761 type remote interface { |
| 655 fetchACL(packagePath string) ([]PackageACL, error) | 762 fetchACL(packagePath string) ([]PackageACL, error) |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 716 } | 823 } |
| 717 | 824 |
| 718 // buildInstanceIDMap builds mapping {package name -> instance ID}. | 825 // buildInstanceIDMap builds mapping {package name -> instance ID}. |
| 719 func buildInstanceIDMap(pins []common.Pin) map[string]string { | 826 func buildInstanceIDMap(pins []common.Pin) map[string]string { |
| 720 out := map[string]string{} | 827 out := map[string]string{} |
| 721 for _, p := range pins { | 828 for _, p := range pins { |
| 722 out[p.PackageName] = p.InstanceID | 829 out[p.PackageName] = p.InstanceID |
| 723 } | 830 } |
| 724 return out | 831 return out |
| 725 } | 832 } |
| OLD | NEW |