github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/jujuclient/file.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package jujuclient provides functionality to support 5 // connections to Juju such as controllers cache, accounts cache, etc. 6 7 package jujuclient 8 9 import ( 10 "time" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/mutex" 15 "github.com/juju/utils/clock" 16 17 "github.com/juju/juju/cloud" 18 ) 19 20 var _ ClientStore = (*store)(nil) 21 22 var logger = loggo.GetLogger("juju.jujuclient") 23 24 // A second should be enough to write or read any files. But 25 // some disks are slow when under load, so lets give the disk a 26 // reasonable time to get the lock. 27 var lockTimeout = 5 * time.Second 28 29 // NewFileClientStore returns a new filesystem-based client store 30 // that manages files in $XDG_DATA_HOME/juju. 31 func NewFileClientStore() ClientStore { 32 return &store{} 33 } 34 35 // NewFileCredentialStore returns a new filesystem-based credentials store 36 // that manages credentials in $XDG_DATA_HOME/juju. 37 func NewFileCredentialStore() CredentialStore { 38 return &store{} 39 } 40 41 type store struct{} 42 43 func (s *store) acquireLock() (mutex.Releaser, error) { 44 const lockName = "store-lock" 45 spec := mutex.Spec{ 46 Name: lockName, 47 Clock: clock.WallClock, 48 Delay: 20 * time.Millisecond, 49 Timeout: lockTimeout, 50 } 51 releaser, err := mutex.Acquire(spec) 52 if err != nil { 53 return nil, errors.Trace(err) 54 } 55 return releaser, nil 56 } 57 58 // AllControllers implements ControllersGetter. 59 func (s *store) AllControllers() (map[string]ControllerDetails, error) { 60 releaser, err := s.acquireLock() 61 if err != nil { 62 return nil, errors.Annotate(err, "cannot read all controllers") 63 } 64 defer releaser.Release() 65 controllers, err := ReadControllersFile(JujuControllersPath()) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 return controllers.Controllers, nil 70 } 71 72 // CurrentController implements ControllersGetter. 73 func (s *store) CurrentController() (string, error) { 74 releaser, err := s.acquireLock() 75 if err != nil { 76 return "", errors.Annotate(err, "cannot get current controller name") 77 } 78 defer releaser.Release() 79 controllers, err := ReadControllersFile(JujuControllersPath()) 80 if err != nil { 81 return "", errors.Trace(err) 82 } 83 if controllers.CurrentController == "" { 84 return "", errors.NotFoundf("current controller") 85 } 86 return controllers.CurrentController, nil 87 } 88 89 // ControllerByName implements ControllersGetter. 90 func (s *store) ControllerByName(name string) (*ControllerDetails, error) { 91 if err := ValidateControllerName(name); err != nil { 92 return nil, errors.Trace(err) 93 } 94 95 releaser, err := s.acquireLock() 96 if err != nil { 97 return nil, errors.Annotatef(err, "cannot read controller %v", name) 98 } 99 defer releaser.Release() 100 101 controllers, err := ReadControllersFile(JujuControllersPath()) 102 if err != nil { 103 return nil, errors.Trace(err) 104 } 105 if result, ok := controllers.Controllers[name]; ok { 106 return &result, nil 107 } 108 return nil, errors.NotFoundf("controller %s", name) 109 } 110 111 // AddController implements ControllerUpdater. 112 func (s *store) AddController(name string, details ControllerDetails) error { 113 if err := ValidateControllerName(name); err != nil { 114 return errors.Trace(err) 115 } 116 if err := ValidateControllerDetails(details); err != nil { 117 return errors.Trace(err) 118 } 119 120 releaser, err := s.acquireLock() 121 if err != nil { 122 return errors.Annotatef(err, "cannot add controller %v", name) 123 } 124 defer releaser.Release() 125 126 all, err := ReadControllersFile(JujuControllersPath()) 127 if err != nil { 128 return errors.Annotate(err, "cannot get controllers") 129 } 130 131 if len(all.Controllers) == 0 { 132 all.Controllers = make(map[string]ControllerDetails) 133 } 134 135 if _, ok := all.Controllers[name]; ok { 136 return errors.AlreadyExistsf("controller with name %s", name) 137 } 138 139 for k, v := range all.Controllers { 140 if v.ControllerUUID == details.ControllerUUID { 141 return errors.AlreadyExistsf("controller with UUID %s (%s)", 142 details.ControllerUUID, k) 143 } 144 } 145 146 all.Controllers[name] = details 147 return WriteControllersFile(all) 148 } 149 150 // UpdateController implements ControllerUpdater. 151 func (s *store) UpdateController(name string, details ControllerDetails) error { 152 if err := ValidateControllerName(name); err != nil { 153 return errors.Trace(err) 154 } 155 if err := ValidateControllerDetails(details); err != nil { 156 return errors.Trace(err) 157 } 158 159 releaser, err := s.acquireLock() 160 if err != nil { 161 return errors.Annotatef(err, "cannot update controller %v", name) 162 } 163 defer releaser.Release() 164 165 all, err := ReadControllersFile(JujuControllersPath()) 166 if err != nil { 167 return errors.Annotate(err, "cannot get controllers") 168 } 169 170 if len(all.Controllers) == 0 { 171 return errors.NotFoundf("controllers") 172 } 173 174 for k, v := range all.Controllers { 175 if v.ControllerUUID == details.ControllerUUID && k != name { 176 return errors.AlreadyExistsf("controller %s with UUID %s", 177 k, v.ControllerUUID) 178 } 179 } 180 181 if _, ok := all.Controllers[name]; !ok { 182 return errors.NotFoundf("controller %s", name) 183 } 184 185 all.Controllers[name] = details 186 return WriteControllersFile(all) 187 } 188 189 // SetCurrentController implements ControllerUpdater. 190 func (s *store) SetCurrentController(name string) error { 191 if err := ValidateControllerName(name); err != nil { 192 return errors.Trace(err) 193 } 194 195 releaser, err := s.acquireLock() 196 if err != nil { 197 return errors.Annotate(err, "cannot set current controller name") 198 } 199 defer releaser.Release() 200 201 controllers, err := ReadControllersFile(JujuControllersPath()) 202 if err != nil { 203 return errors.Trace(err) 204 } 205 if _, ok := controllers.Controllers[name]; !ok { 206 return errors.NotFoundf("controller %v", name) 207 } 208 if controllers.CurrentController == name { 209 return nil 210 } 211 controllers.CurrentController = name 212 return WriteControllersFile(controllers) 213 } 214 215 // RemoveController implements ControllersRemover 216 func (s *store) RemoveController(name string) error { 217 if err := ValidateControllerName(name); err != nil { 218 return errors.Trace(err) 219 } 220 221 releaser, err := s.acquireLock() 222 if err != nil { 223 return errors.Annotatef(err, "cannot remove controller %v", name) 224 } 225 defer releaser.Release() 226 227 controllers, err := ReadControllersFile(JujuControllersPath()) 228 if err != nil { 229 return errors.Annotate(err, "cannot get controllers") 230 } 231 232 // We remove all controllers with the same UUID as the named one. 233 namedControllerDetails, ok := controllers.Controllers[name] 234 if !ok { 235 return nil 236 } 237 var names []string 238 for name, details := range controllers.Controllers { 239 if details.ControllerUUID == namedControllerDetails.ControllerUUID { 240 names = append(names, name) 241 delete(controllers.Controllers, name) 242 if controllers.CurrentController == name { 243 controllers.CurrentController = "" 244 } 245 } 246 } 247 248 // Remove models for the controller. 249 controllerModels, err := ReadModelsFile(JujuModelsPath()) 250 if err != nil { 251 return errors.Trace(err) 252 } 253 for _, name := range names { 254 if _, ok := controllerModels[name]; ok { 255 delete(controllerModels, name) 256 if err := WriteModelsFile(controllerModels); err != nil { 257 return errors.Trace(err) 258 } 259 } 260 } 261 262 // Remove accounts for the controller. 263 controllerAccounts, err := ReadAccountsFile(JujuAccountsPath()) 264 if err != nil { 265 return errors.Trace(err) 266 } 267 for _, name := range names { 268 if _, ok := controllerAccounts[name]; ok { 269 delete(controllerAccounts, name) 270 if err := WriteAccountsFile(controllerAccounts); err != nil { 271 return errors.Trace(err) 272 } 273 } 274 } 275 276 // Remove bootstrap config for the controller. 277 bootstrapConfigurations, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath()) 278 if err != nil { 279 return errors.Trace(err) 280 } 281 for _, name := range names { 282 if _, ok := bootstrapConfigurations[name]; ok { 283 delete(bootstrapConfigurations, name) 284 if err := WriteBootstrapConfigFile(bootstrapConfigurations); err != nil { 285 return errors.Trace(err) 286 } 287 } 288 } 289 290 // Finally, remove the controllers. This must be done last 291 // so we don't end up with dangling entries in other files. 292 return WriteControllersFile(controllers) 293 } 294 295 // UpdateModel implements ModelUpdater. 296 func (s *store) UpdateModel(controllerName, modelName string, details ModelDetails) error { 297 if err := ValidateControllerName(controllerName); err != nil { 298 return errors.Trace(err) 299 } 300 if err := ValidateModelName(modelName); err != nil { 301 return errors.Trace(err) 302 } 303 if err := ValidateModelDetails(details); err != nil { 304 return errors.Trace(err) 305 } 306 307 releaser, err := s.acquireLock() 308 if err != nil { 309 return errors.Trace(err) 310 } 311 defer releaser.Release() 312 313 return errors.Trace(updateModels( 314 controllerName, 315 func(models *ControllerModels) (bool, error) { 316 oldDetails, ok := models.Models[modelName] 317 if ok && details == oldDetails { 318 return false, nil 319 } 320 models.Models[modelName] = details 321 return true, nil 322 }, 323 )) 324 } 325 326 // SetCurrentModel implements ModelUpdater. 327 func (s *store) SetCurrentModel(controllerName, modelName string) error { 328 if err := ValidateControllerName(controllerName); err != nil { 329 return errors.Trace(err) 330 } 331 if err := ValidateModelName(modelName); err != nil { 332 return errors.Trace(err) 333 } 334 335 releaser, err := s.acquireLock() 336 if err != nil { 337 return errors.Trace(err) 338 } 339 defer releaser.Release() 340 341 return errors.Trace(updateModels( 342 controllerName, 343 func(models *ControllerModels) (bool, error) { 344 if models.CurrentModel == modelName { 345 return false, nil 346 } 347 if _, ok := models.Models[modelName]; !ok { 348 return false, errors.NotFoundf( 349 "model %s:%s", 350 controllerName, 351 modelName, 352 ) 353 } 354 models.CurrentModel = modelName 355 return true, nil 356 }, 357 )) 358 } 359 360 // AllModels implements ModelGetter. 361 func (s *store) AllModels(controllerName string) (map[string]ModelDetails, error) { 362 if err := ValidateControllerName(controllerName); err != nil { 363 return nil, errors.Trace(err) 364 } 365 366 releaser, err := s.acquireLock() 367 if err != nil { 368 return nil, errors.Trace(err) 369 } 370 defer releaser.Release() 371 372 all, err := ReadModelsFile(JujuModelsPath()) 373 if err != nil { 374 return nil, errors.Trace(err) 375 } 376 controllerModels, ok := all[controllerName] 377 if !ok { 378 return nil, errors.NotFoundf( 379 "models for controller %s", 380 controllerName, 381 ) 382 } 383 return controllerModels.Models, nil 384 } 385 386 // CurrentModel implements ModelGetter. 387 func (s *store) CurrentModel(controllerName string) (string, error) { 388 if err := ValidateControllerName(controllerName); err != nil { 389 return "", errors.Trace(err) 390 } 391 392 releaser, err := s.acquireLock() 393 if err != nil { 394 return "", errors.Trace(err) 395 } 396 defer releaser.Release() 397 398 all, err := ReadModelsFile(JujuModelsPath()) 399 if err != nil { 400 return "", errors.Trace(err) 401 } 402 controllerModels, ok := all[controllerName] 403 if !ok { 404 return "", errors.NotFoundf( 405 "current model for controller %s", 406 controllerName, 407 ) 408 } 409 if controllerModels.CurrentModel == "" { 410 return "", errors.NotFoundf( 411 "current model for controller %s", 412 controllerName, 413 ) 414 } 415 return controllerModels.CurrentModel, nil 416 } 417 418 // ModelByName implements ModelGetter. 419 func (s *store) ModelByName(controllerName, modelName string) (*ModelDetails, error) { 420 if err := ValidateControllerName(controllerName); err != nil { 421 return nil, errors.Trace(err) 422 } 423 if err := ValidateModelName(modelName); err != nil { 424 return nil, errors.Trace(err) 425 } 426 427 releaser, err := s.acquireLock() 428 if err != nil { 429 return nil, errors.Trace(err) 430 } 431 defer releaser.Release() 432 433 all, err := ReadModelsFile(JujuModelsPath()) 434 if err != nil { 435 return nil, errors.Trace(err) 436 } 437 controllerModels, ok := all[controllerName] 438 if !ok { 439 return nil, errors.NotFoundf( 440 "models for controller %s", 441 controllerName, 442 ) 443 } 444 details, ok := controllerModels.Models[modelName] 445 if !ok { 446 return nil, errors.NotFoundf( 447 "model %s:%s", 448 controllerName, 449 modelName, 450 ) 451 } 452 return &details, nil 453 } 454 455 // RemoveModel implements ModelRemover. 456 func (s *store) RemoveModel(controllerName, modelName string) error { 457 if err := ValidateControllerName(controllerName); err != nil { 458 return errors.Trace(err) 459 } 460 if err := ValidateModelName(modelName); err != nil { 461 return errors.Trace(err) 462 } 463 464 releaser, err := s.acquireLock() 465 if err != nil { 466 return errors.Trace(err) 467 } 468 defer releaser.Release() 469 470 return errors.Trace(updateModels( 471 controllerName, 472 func(models *ControllerModels) (bool, error) { 473 if _, ok := models.Models[modelName]; !ok { 474 return false, errors.NotFoundf( 475 "model %s:%s", 476 controllerName, 477 modelName, 478 ) 479 } 480 delete(models.Models, modelName) 481 if models.CurrentModel == modelName { 482 models.CurrentModel = "" 483 } 484 return true, nil 485 }, 486 )) 487 } 488 489 func updateModels( 490 controllerName string, 491 update func(*ControllerModels) (bool, error), 492 ) error { 493 all, err := ReadModelsFile(JujuModelsPath()) 494 if err != nil { 495 return errors.Trace(err) 496 } 497 controllerModels, ok := all[controllerName] 498 if !ok { 499 if all == nil { 500 all = make(map[string]*ControllerModels) 501 } 502 controllerModels = &ControllerModels{} 503 all[controllerName] = controllerModels 504 } 505 if controllerModels.Models == nil { 506 controllerModels.Models = make(map[string]ModelDetails) 507 } 508 updated, err := update(controllerModels) 509 if err != nil { 510 return errors.Trace(err) 511 } 512 if updated { 513 return errors.Trace(WriteModelsFile(all)) 514 } 515 return nil 516 } 517 518 // UpdateAccount implements AccountUpdater. 519 func (s *store) UpdateAccount(controllerName string, details AccountDetails) error { 520 if err := ValidateControllerName(controllerName); err != nil { 521 return errors.Trace(err) 522 } 523 if err := ValidateAccountDetails(details); err != nil { 524 return errors.Trace(err) 525 } 526 527 releaser, err := s.acquireLock() 528 if err != nil { 529 return errors.Trace(err) 530 } 531 defer releaser.Release() 532 533 accounts, err := ReadAccountsFile(JujuAccountsPath()) 534 if err != nil { 535 return errors.Trace(err) 536 } 537 if accounts == nil { 538 accounts = make(map[string]AccountDetails) 539 } 540 if oldDetails, ok := accounts[controllerName]; ok && details == oldDetails { 541 return nil 542 } else { 543 // Only update last known access if it has a value. 544 if details.LastKnownAccess == "" { 545 details.LastKnownAccess = oldDetails.LastKnownAccess 546 } 547 } 548 549 accounts[controllerName] = details 550 return errors.Trace(WriteAccountsFile(accounts)) 551 } 552 553 // AccountByName implements AccountGetter. 554 func (s *store) AccountDetails(controllerName string) (*AccountDetails, error) { 555 if err := ValidateControllerName(controllerName); err != nil { 556 return nil, errors.Trace(err) 557 } 558 559 releaser, err := s.acquireLock() 560 if err != nil { 561 return nil, errors.Trace(err) 562 } 563 defer releaser.Release() 564 565 accounts, err := ReadAccountsFile(JujuAccountsPath()) 566 if err != nil { 567 return nil, errors.Trace(err) 568 } 569 details, ok := accounts[controllerName] 570 if !ok { 571 return nil, errors.NotFoundf("account details for controller %s", controllerName) 572 } 573 return &details, nil 574 } 575 576 // RemoveAccount implements AccountRemover. 577 func (s *store) RemoveAccount(controllerName string) error { 578 if err := ValidateControllerName(controllerName); err != nil { 579 return errors.Trace(err) 580 } 581 582 releaser, err := s.acquireLock() 583 if err != nil { 584 return errors.Trace(err) 585 } 586 defer releaser.Release() 587 588 accounts, err := ReadAccountsFile(JujuAccountsPath()) 589 if err != nil { 590 return errors.Trace(err) 591 } 592 if _, ok := accounts[controllerName]; !ok { 593 return errors.NotFoundf("account details for controller %s", controllerName) 594 } 595 596 delete(accounts, controllerName) 597 return errors.Trace(WriteAccountsFile(accounts)) 598 } 599 600 // UpdateCredential implements CredentialUpdater. 601 func (s *store) UpdateCredential(cloudName string, details cloud.CloudCredential) error { 602 releaser, err := s.acquireLock() 603 if err != nil { 604 return errors.Annotatef(err, "cannot update credentials for %v", cloudName) 605 } 606 defer releaser.Release() 607 608 all, err := ReadCredentialsFile(JujuCredentialsPath()) 609 if err != nil { 610 return errors.Annotate(err, "cannot get credentials") 611 } 612 613 if len(all) == 0 { 614 all = make(map[string]cloud.CloudCredential) 615 } 616 617 // Clear the default credential if we are removing that one. 618 if existing, ok := all[cloudName]; ok && existing.DefaultCredential != "" { 619 stillHaveDefault := false 620 for name := range details.AuthCredentials { 621 if name == existing.DefaultCredential { 622 stillHaveDefault = true 623 break 624 } 625 } 626 if !stillHaveDefault { 627 details.DefaultCredential = "" 628 } 629 } 630 631 all[cloudName] = details 632 return WriteCredentialsFile(all) 633 } 634 635 // CredentialForCloud implements CredentialGetter. 636 func (s *store) CredentialForCloud(cloudName string) (*cloud.CloudCredential, error) { 637 cloudCredentials, err := s.AllCredentials() 638 if err != nil { 639 return nil, errors.Trace(err) 640 } 641 credentials, ok := cloudCredentials[cloudName] 642 if !ok { 643 return nil, errors.NotFoundf("credentials for cloud %s", cloudName) 644 } 645 return &credentials, nil 646 } 647 648 // AllCredentials implements CredentialGetter. 649 func (s *store) AllCredentials() (map[string]cloud.CloudCredential, error) { 650 cloudCredentials, err := ReadCredentialsFile(JujuCredentialsPath()) 651 if err != nil { 652 return nil, errors.Trace(err) 653 } 654 return cloudCredentials, nil 655 } 656 657 // UpdateBootstrapConfig implements BootstrapConfigUpdater. 658 func (s *store) UpdateBootstrapConfig(controllerName string, cfg BootstrapConfig) error { 659 if err := ValidateControllerName(controllerName); err != nil { 660 return errors.Trace(err) 661 } 662 if err := ValidateBootstrapConfig(cfg); err != nil { 663 return errors.Trace(err) 664 } 665 666 releaser, err := s.acquireLock() 667 if err != nil { 668 return errors.Annotatef(err, "cannot update bootstrap config for controller %s", controllerName) 669 } 670 defer releaser.Release() 671 672 all, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath()) 673 if err != nil { 674 return errors.Annotate(err, "cannot get bootstrap config") 675 } 676 677 if all == nil { 678 all = make(map[string]BootstrapConfig) 679 } 680 all[controllerName] = cfg 681 return WriteBootstrapConfigFile(all) 682 } 683 684 // BootstrapConfigForController implements BootstrapConfigGetter. 685 func (s *store) BootstrapConfigForController(controllerName string) (*BootstrapConfig, error) { 686 configs, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath()) 687 if err != nil { 688 return nil, errors.Trace(err) 689 } 690 cfg, ok := configs[controllerName] 691 if !ok { 692 return nil, errors.NotFoundf("bootstrap config for controller %s", controllerName) 693 } 694 if cfg.CloudType == "" { 695 // TODO(axw) 2016-07-25 #1603841 696 // Drop this when we get to 2.0. This exists only for 697 // compatibility with previous beta releases. 698 cfg.CloudType, _ = cfg.Config["type"].(string) 699 } 700 return &cfg, nil 701 }