github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "crypto/sha256" 11 "fmt" 12 "os" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "time" 17 18 "github.com/juju/clock" 19 "github.com/juju/collections/set" 20 "github.com/juju/errors" 21 "github.com/juju/mutex/v2" 22 cookiejar "github.com/juju/persistent-cookiejar" 23 24 "github.com/juju/juju/cloud" 25 "github.com/juju/juju/core/model" 26 "github.com/juju/juju/juju/osenv" 27 ) 28 29 var ( 30 _ ClientStore = (*store)(nil) 31 32 // A second should be enough to write or read any files. But 33 // some disks are slow when under load, so lets give the disk a 34 // reasonable time to get the lock. 35 lockTimeout = 5 * time.Second 36 ) 37 38 // NewFileClientStore returns a new filesystem-based client store 39 // that manages files in $XDG_DATA_HOME/juju. 40 func NewFileClientStore() ClientStore { 41 return &store{ 42 lockName: generateStoreLockName(), 43 } 44 } 45 46 // NewFileCredentialStore returns a new filesystem-based credentials store 47 // that manages credentials in $XDG_DATA_HOME/juju. 48 func NewFileCredentialStore() CredentialStore { 49 return &store{ 50 lockName: generateStoreLockName(), 51 } 52 } 53 54 type store struct { 55 lockName string 56 } 57 58 // generateStoreLockName uses part of the hash of the controller path as the 59 // name of the lock. This is to avoid contention between multiple users on a 60 // single machine with different controller files, but also helps with 61 // contention in tests. 62 func generateStoreLockName() string { 63 h := sha256.New() 64 _, _ = h.Write([]byte(JujuControllersPath())) 65 fullHash := fmt.Sprintf("%x", h.Sum(nil)) 66 return fmt.Sprintf("store-lock-%x", fullHash[:8]) 67 } 68 69 func (s *store) acquireLock() (mutex.Releaser, error) { 70 spec := mutex.Spec{ 71 Name: s.lockName, 72 Clock: clock.WallClock, 73 Delay: 20 * time.Millisecond, 74 Timeout: lockTimeout, 75 } 76 releaser, err := mutex.Acquire(spec) 77 if err != nil { 78 return nil, errors.Trace(err) 79 } 80 return releaser, nil 81 } 82 83 // AllControllers implements ControllersGetter. 84 func (s *store) AllControllers() (map[string]ControllerDetails, error) { 85 releaser, err := s.acquireLock() 86 if err != nil { 87 return nil, errors.Annotate(err, 88 "cannot acquire lock file to read all the controllers", 89 ) 90 } 91 defer releaser.Release() 92 controllers, err := ReadControllersFile(JujuControllersPath()) 93 if err != nil { 94 return nil, errors.Trace(err) 95 } 96 return controllers.Controllers, nil 97 } 98 99 // CurrentController implements ControllersGetter. 100 func (s *store) CurrentController() (string, error) { 101 releaser, err := s.acquireLock() 102 if err != nil { 103 return "", errors.Annotate(err, 104 "cannot acquire lock file to get the current controller name", 105 ) 106 } 107 defer releaser.Release() 108 controllers, err := ReadControllersFile(JujuControllersPath()) 109 if err != nil { 110 return "", errors.Trace(err) 111 } 112 if controllers.CurrentController == "" { 113 return "", errors.NotFoundf("current controller") 114 } 115 return controllers.CurrentController, nil 116 } 117 118 // ControllerByName implements ControllersGetter. 119 func (s *store) ControllerByName(name string) (*ControllerDetails, error) { 120 if err := ValidateControllerName(name); err != nil { 121 return nil, errors.Trace(err) 122 } 123 124 releaser, err := s.acquireLock() 125 if err != nil { 126 return nil, errors.Annotatef(err, 127 "cannot acquire lock file to read controller %s", name, 128 ) 129 } 130 defer releaser.Release() 131 132 controllers, err := ReadControllersFile(JujuControllersPath()) 133 if err != nil { 134 return nil, errors.Trace(err) 135 } 136 if result, ok := controllers.Controllers[name]; ok { 137 return &result, nil 138 } 139 return nil, errors.NotFoundf("controller %s", name) 140 } 141 142 // ControllerByEndpoints implements ControllersGetter. 143 func (s *store) ControllerByAPIEndpoints(endpoints ...string) (*ControllerDetails, string, error) { 144 releaser, err := s.acquireLock() 145 if err != nil { 146 return nil, "", errors.Annotatef(err, "cannot acquire lock file to read controllers") 147 } 148 defer releaser.Release() 149 150 controllers, err := ReadControllersFile(JujuControllersPath()) 151 if err != nil { 152 return nil, "", errors.Trace(err) 153 } 154 155 matchEps := set.NewStrings(endpoints...) 156 for name, ctrl := range controllers.Controllers { 157 if matchEps.Intersection(set.NewStrings(ctrl.APIEndpoints...)).IsEmpty() { 158 continue 159 } 160 161 return &ctrl, name, nil 162 } 163 return nil, "", errors.NotFoundf("controller with API endpoints %v", endpoints) 164 } 165 166 // AddController implements ControllerUpdater. 167 func (s *store) AddController(name string, details ControllerDetails) error { 168 if err := ValidateControllerName(name); err != nil { 169 return errors.Trace(err) 170 } 171 if err := ValidateControllerDetails(details); err != nil { 172 return errors.Trace(err) 173 } 174 175 releaser, err := s.acquireLock() 176 if err != nil { 177 return errors.Annotatef(err, 178 "cannot acquire lock file to add controller %s", name, 179 ) 180 } 181 defer releaser.Release() 182 183 all, err := ReadControllersFile(JujuControllersPath()) 184 if err != nil { 185 return errors.Annotate(err, "cannot get controllers") 186 } 187 188 if len(all.Controllers) == 0 { 189 all.Controllers = make(map[string]ControllerDetails) 190 } 191 192 if _, ok := all.Controllers[name]; ok { 193 return errors.AlreadyExistsf("controller with name %s", name) 194 } 195 196 for k, v := range all.Controllers { 197 if v.ControllerUUID == details.ControllerUUID { 198 return errors.AlreadyExistsf("controller with UUID %s (%s)", 199 details.ControllerUUID, k) 200 } 201 } 202 203 all.Controllers[name] = details 204 return WriteControllersFile(all) 205 } 206 207 // UpdateController implements ControllerUpdater. 208 func (s *store) UpdateController(name string, details ControllerDetails) error { 209 if err := ValidateControllerName(name); err != nil { 210 return errors.Trace(err) 211 } 212 if err := ValidateControllerDetails(details); err != nil { 213 return errors.Trace(err) 214 } 215 216 releaser, err := s.acquireLock() 217 if err != nil { 218 return errors.Annotatef(err, 219 "cannot acquire lock file to update controller %s", name, 220 ) 221 } 222 defer releaser.Release() 223 224 all, err := ReadControllersFile(JujuControllersPath()) 225 if err != nil { 226 return errors.Annotate(err, "cannot get controllers") 227 } 228 229 if len(all.Controllers) == 0 { 230 return errors.NotFoundf("controllers") 231 } 232 233 for k, v := range all.Controllers { 234 if v.ControllerUUID == details.ControllerUUID && k != name { 235 return errors.AlreadyExistsf("controller %s with UUID %s", 236 k, v.ControllerUUID) 237 } 238 } 239 240 if _, ok := all.Controllers[name]; !ok { 241 return errors.NotFoundf("controller %s", name) 242 } 243 244 all.Controllers[name] = details 245 return WriteControllersFile(all) 246 } 247 248 // SetCurrentController implements ControllerUpdater. 249 func (s *store) SetCurrentController(name string) error { 250 if err := ValidateControllerName(name); err != nil { 251 return errors.Trace(err) 252 } 253 254 releaser, err := s.acquireLock() 255 if err != nil { 256 return errors.Annotate(err, 257 "cannot acquire lock file to set the current controller name", 258 ) 259 } 260 defer releaser.Release() 261 262 controllers, err := ReadControllersFile(JujuControllersPath()) 263 if err != nil { 264 return errors.Trace(err) 265 } 266 if _, ok := controllers.Controllers[name]; !ok { 267 return errors.NotFoundf("controller %v", name) 268 } 269 if controllers.CurrentController == name { 270 return nil 271 } 272 controllers.CurrentController = name 273 return WriteControllersFile(controllers) 274 } 275 276 // RemoveController implements ControllersRemover 277 func (s *store) RemoveController(name string) error { 278 if err := ValidateControllerName(name); err != nil { 279 return errors.Trace(err) 280 } 281 282 releaser, err := s.acquireLock() 283 if err != nil { 284 return errors.Annotatef(err, 285 "cannot acquire lock file to remove controller %s", name, 286 ) 287 } 288 defer releaser.Release() 289 290 controllers, err := ReadControllersFile(JujuControllersPath()) 291 if err != nil { 292 return errors.Annotate(err, "cannot get controllers") 293 } 294 295 // We remove all controllers with the same UUID as the named one. 296 namedControllerDetails, ok := controllers.Controllers[name] 297 if !ok { 298 return nil 299 } 300 var names []string 301 for name, details := range controllers.Controllers { 302 if details.ControllerUUID == namedControllerDetails.ControllerUUID { 303 names = append(names, name) 304 delete(controllers.Controllers, name) 305 if controllers.CurrentController == name { 306 controllers.CurrentController = "" 307 } 308 } 309 } 310 311 // Remove models for the controller. 312 controllerModels, err := ReadModelsFile(JujuModelsPath()) 313 if err != nil { 314 return errors.Trace(err) 315 } 316 for _, name := range names { 317 if _, ok := controllerModels[name]; ok { 318 delete(controllerModels, name) 319 if err := WriteModelsFile(controllerModels); err != nil { 320 return errors.Trace(err) 321 } 322 } 323 } 324 325 // Remove accounts for the controller. 326 controllerAccounts, err := ReadAccountsFile(JujuAccountsPath()) 327 if err != nil { 328 return errors.Trace(err) 329 } 330 for _, name := range names { 331 if _, ok := controllerAccounts[name]; ok { 332 delete(controllerAccounts, name) 333 if err := WriteAccountsFile(controllerAccounts); err != nil { 334 return errors.Trace(err) 335 } 336 } 337 } 338 339 // Remove bootstrap config for the controller. 340 bootstrapConfigurations, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath()) 341 if err != nil { 342 return errors.Trace(err) 343 } 344 for _, name := range names { 345 if _, ok := bootstrapConfigurations[name]; ok { 346 delete(bootstrapConfigurations, name) 347 if err := WriteBootstrapConfigFile(bootstrapConfigurations); err != nil { 348 return errors.Trace(err) 349 } 350 } 351 } 352 353 // Remove the controller cookie jars. 354 for _, name := range names { 355 err := os.Remove(JujuCookiePath(name)) 356 if err != nil && !os.IsNotExist(err) { 357 return errors.Trace(err) 358 } 359 } 360 361 // Finally, remove the controllers. This must be done last 362 // so we don't end up with dangling entries in other files. 363 return WriteControllersFile(controllers) 364 } 365 366 // UpdateModel implements ModelUpdater. 367 func (s *store) UpdateModel(controllerName, modelName string, details ModelDetails) error { 368 if err := ValidateControllerName(controllerName); err != nil { 369 return errors.Trace(err) 370 } 371 if err := ValidateModel(modelName, details); err != nil { 372 return errors.Trace(err) 373 } 374 375 releaser, err := s.acquireLock() 376 if err != nil { 377 return errors.Annotatef(err, 378 "cannot acquire lock file for updating model %s on controller %s", modelName, controllerName, 379 ) 380 } 381 defer releaser.Release() 382 383 return errors.Trace(updateModels( 384 controllerName, 385 func(models *ControllerModels) (bool, error) { 386 oldDetails, ok := models.Models[modelName] 387 if ok && details == oldDetails { 388 return false, nil 389 } 390 if ok && oldDetails.ModelType == "" && details.ModelType != model.IAAS || 391 oldDetails.ModelType != "" && oldDetails.ModelType != details.ModelType { 392 oldModelType := oldDetails.ModelType 393 if oldModelType == "" { 394 oldModelType = model.IAAS 395 } 396 return false, errors.Errorf( 397 "model type was %q, cannot change to %q", oldModelType, details.ModelType) 398 } 399 models.Models[modelName] = details 400 return true, nil 401 }, 402 )) 403 } 404 405 // SetCurrentModel implements ModelUpdater. 406 func (s *store) SetCurrentModel(controllerName, modelName string) error { 407 if err := ValidateControllerName(controllerName); err != nil { 408 return errors.Trace(err) 409 } 410 411 releaser, err := s.acquireLock() 412 if err != nil { 413 return errors.Annotatef(err, 414 "cannot acquire lock file for setting current model %s on controller %s", modelName, controllerName, 415 ) 416 } 417 defer releaser.Release() 418 419 if modelName != "" { 420 if err := ValidateModelName(modelName); err != nil { 421 return errors.Trace(err) 422 } 423 } 424 return errors.Trace(updateModels( 425 controllerName, 426 func(models *ControllerModels) (bool, error) { 427 if modelName == "" { 428 // We just want to reset 429 models.CurrentModel = "" 430 return true, nil 431 } 432 if _, ok := models.Models[modelName]; !ok { 433 return false, errors.NotFoundf( 434 "model %s:%s", 435 controllerName, 436 modelName, 437 ) 438 } 439 models.CurrentModel = modelName 440 return true, nil 441 }, 442 )) 443 } 444 445 // AllModels implements ModelGetter. 446 func (s *store) AllModels(controllerName string) (map[string]ModelDetails, error) { 447 if err := ValidateControllerName(controllerName); err != nil { 448 return nil, errors.Trace(err) 449 } 450 451 releaser, err := s.acquireLock() 452 if err != nil { 453 return nil, errors.Annotatef(err, 454 "cannot acquire lock file for getting all models for controller %s", controllerName, 455 ) 456 } 457 defer releaser.Release() 458 459 all, err := ReadModelsFile(JujuModelsPath()) 460 if err != nil { 461 return nil, errors.Trace(err) 462 } 463 controllerModels, ok := all[controllerName] 464 if !ok { 465 return nil, errors.NotFoundf( 466 "models for controller %s", 467 controllerName, 468 ) 469 } 470 return controllerModels.Models, nil 471 } 472 473 // CurrentModel implements ModelGetter. 474 func (s *store) CurrentModel(controllerName string) (string, error) { 475 if err := ValidateControllerName(controllerName); err != nil { 476 return "", errors.Trace(err) 477 } 478 479 releaser, err := s.acquireLock() 480 if err != nil { 481 return "", errors.Annotatef(err, 482 "cannot acquire lock file for getting current model for controller %s", controllerName, 483 ) 484 } 485 defer releaser.Release() 486 487 all, err := ReadModelsFile(JujuModelsPath()) 488 if err != nil { 489 return "", errors.Trace(err) 490 } 491 controllerModels, ok := all[controllerName] 492 if !ok { 493 return "", errors.NotFoundf( 494 "current model for controller %s", 495 controllerName, 496 ) 497 } 498 499 var controller bool 500 var modelNames []string 501 for ns := range controllerModels.Models { 502 name, _, err := SplitModelName(ns) 503 if err != nil { 504 continue 505 } 506 if name == "controller" { 507 controller = true 508 continue 509 } 510 modelNames = append(modelNames, controllerName+":"+ns) 511 } 512 513 if controllerModels.CurrentModel == "" { 514 num := len(modelNames) 515 if num == 0 { 516 if !controller { 517 return "", errors.NotFoundf( 518 "current model for controller %s", 519 controllerName, 520 ) 521 } 522 return "", errors.NewNotFound(nil, `No selected model. 523 524 Only the controller model exists. Use "juju add-model" to create an initial model. 525 `) 526 } 527 528 msg := `No selected model. 529 530 Use "juju switch" to select one of the following models: 531 532 - ` + strings.Join(modelNames, "\n - ") 533 return "", errors.NewNotFound(nil, msg) 534 } 535 return controllerModels.CurrentModel, nil 536 } 537 538 // ModelByName implements ModelGetter. 539 func (s *store) ModelByName(controllerName, modelName string) (*ModelDetails, error) { 540 if err := ValidateControllerName(controllerName); err != nil { 541 return nil, errors.Trace(err) 542 } 543 if err := ValidateModelName(modelName); err != nil { 544 return nil, errors.Trace(err) 545 } 546 547 releaser, err := s.acquireLock() 548 if err != nil { 549 return nil, errors.Annotatef(err, 550 "cannot acquire lock file for getting model %s for controller %s", modelName, controllerName, 551 ) 552 } 553 defer releaser.Release() 554 555 all, err := ReadModelsFile(JujuModelsPath()) 556 if err != nil { 557 return nil, errors.Trace(err) 558 } 559 controllerModels, ok := all[controllerName] 560 if !ok { 561 return nil, errors.NotFoundf( 562 "model %s:%s", 563 controllerName, 564 modelName, 565 ) 566 } 567 details, ok := controllerModels.Models[modelName] 568 if !ok { 569 return nil, errors.NotFoundf( 570 "model %s:%s", 571 controllerName, 572 modelName, 573 ) 574 } 575 return &details, nil 576 } 577 578 // RemoveModel implements ModelRemover. 579 func (s *store) RemoveModel(controllerName, modelName string) error { 580 if err := ValidateControllerName(controllerName); err != nil { 581 return errors.Trace(err) 582 } 583 if err := ValidateModelName(modelName); err != nil { 584 return errors.Trace(err) 585 } 586 587 releaser, err := s.acquireLock() 588 if err != nil { 589 return errors.Annotatef(err, 590 "cannot acquire lock file for removing model %s on controller %s", modelName, controllerName, 591 ) 592 } 593 defer releaser.Release() 594 595 return errors.Trace(updateModels( 596 controllerName, 597 func(models *ControllerModels) (bool, error) { 598 if _, ok := models.Models[modelName]; !ok { 599 return false, errors.NotFoundf( 600 "model %s:%s", 601 controllerName, 602 modelName, 603 ) 604 } 605 delete(models.Models, modelName) 606 return true, nil 607 }, 608 )) 609 } 610 611 type updateModelFunc func(storedModels *ControllerModels) (bool, error) 612 613 func updateModels(controllerName string, update updateModelFunc) error { 614 all, err := ReadModelsFile(JujuModelsPath()) 615 if err != nil { 616 return errors.Trace(err) 617 } 618 controllerModels, ok := all[controllerName] 619 if !ok { 620 if all == nil { 621 all = make(map[string]*ControllerModels) 622 } 623 controllerModels = &ControllerModels{} 624 all[controllerName] = controllerModels 625 } 626 if controllerModels.Models == nil { 627 controllerModels.Models = make(map[string]ModelDetails) 628 } 629 updated, err := update(controllerModels) 630 if err != nil { 631 return errors.Trace(err) 632 } 633 if updated { 634 return errors.Trace(WriteModelsFile(all)) 635 } 636 return nil 637 } 638 639 // SetModels implements ModelUpdater. 640 func (s *store) SetModels(controllerName string, models map[string]ModelDetails) error { 641 if err := ValidateControllerName(controllerName); err != nil { 642 return errors.Trace(err) 643 } 644 645 for modelName, details := range models { 646 if err := ValidateModel(modelName, details); err != nil { 647 return errors.Trace(err) 648 } 649 } 650 651 releaser, err := s.acquireLock() 652 if err != nil { 653 return errors.Annotatef(err, 654 "cannot acquire lock file for setting models on controller %s", controllerName, 655 ) 656 } 657 defer releaser.Release() 658 659 err = updateModels(controllerName, func(storedModels *ControllerModels) (bool, error) { 660 changed := len(storedModels.Models) != len(models) 661 // Add or update controller models based on a new collection. 662 for modelName, details := range models { 663 oldDetails, ok := storedModels.Models[modelName] 664 if ok && details == oldDetails { 665 continue 666 } 667 if details.ActiveBranch == "" { 668 if oldDetails.ActiveBranch != "" { 669 details.ActiveBranch = oldDetails.ActiveBranch 670 } else { 671 details.ActiveBranch = model.GenerationMaster 672 } 673 } 674 storedModels.Models[modelName] = details 675 changed = true 676 } 677 // Delete models that are not in the new collection. 678 for modelName := range storedModels.Models { 679 if _, ok := models[modelName]; !ok { 680 delete(storedModels.Models, modelName) 681 } 682 } 683 return changed, nil 684 }) 685 if err != nil { 686 return errors.Trace(err) 687 } 688 return nil 689 } 690 691 // UpdateAccount implements AccountUpdater. 692 func (s *store) UpdateAccount(controllerName string, details AccountDetails) error { 693 if err := ValidateControllerName(controllerName); err != nil { 694 return errors.Trace(err) 695 } 696 if err := ValidateAccountDetails(details); err != nil { 697 return errors.Trace(err) 698 } 699 700 releaser, err := s.acquireLock() 701 if err != nil { 702 return errors.Annotatef(err, 703 "cannot acquire lock file for updating an account on controller %s", controllerName, 704 ) 705 } 706 defer releaser.Release() 707 708 accounts, err := ReadAccountsFile(JujuAccountsPath()) 709 if err != nil { 710 return errors.Trace(err) 711 } 712 if accounts == nil { 713 accounts = make(map[string]AccountDetails) 714 } 715 if oldDetails, ok := accounts[controllerName]; ok && reflect.DeepEqual(details, oldDetails) { 716 return nil 717 } else { 718 // Only update last known access if it has a value. 719 if details.LastKnownAccess == "" { 720 details.LastKnownAccess = oldDetails.LastKnownAccess 721 } 722 } 723 724 accounts[controllerName] = details 725 return errors.Trace(WriteAccountsFile(accounts)) 726 } 727 728 // AccountDetails implements AccountGetter. 729 func (s *store) AccountDetails(controllerName string) (*AccountDetails, error) { 730 if err := ValidateControllerName(controllerName); err != nil { 731 return nil, errors.Trace(err) 732 } 733 734 releaser, err := s.acquireLock() 735 if err != nil { 736 return nil, errors.Annotatef(err, 737 "cannot acquire lock file for getting an account details on controller %s", controllerName, 738 ) 739 } 740 defer releaser.Release() 741 742 accounts, err := ReadAccountsFile(JujuAccountsPath()) 743 if err != nil { 744 return nil, errors.Trace(err) 745 } 746 details, ok := accounts[controllerName] 747 if !ok { 748 return nil, errors.NotFoundf("account details for controller %s", controllerName) 749 } 750 return &details, nil 751 } 752 753 // RemoveAccount implements AccountRemover. 754 func (s *store) RemoveAccount(controllerName string) error { 755 if err := ValidateControllerName(controllerName); err != nil { 756 return errors.Trace(err) 757 } 758 759 releaser, err := s.acquireLock() 760 if err != nil { 761 return errors.Annotatef(err, 762 "cannot acquire lock file for removing an account on controller %s", controllerName, 763 ) 764 } 765 defer releaser.Release() 766 767 accounts, err := ReadAccountsFile(JujuAccountsPath()) 768 if err != nil { 769 return errors.Trace(err) 770 } 771 if _, ok := accounts[controllerName]; !ok { 772 return errors.NotFoundf("account details for controller %s", controllerName) 773 } 774 775 delete(accounts, controllerName) 776 return errors.Trace(WriteAccountsFile(accounts)) 777 } 778 779 // UpdateCredential implements CredentialUpdater. 780 func (s *store) UpdateCredential(cloudName string, details cloud.CloudCredential) error { 781 releaser, err := s.acquireLock() 782 if err != nil { 783 return errors.Annotatef(err, 784 "cannot acquire lock file for updating credentials for %s", cloudName, 785 ) 786 } 787 defer releaser.Release() 788 789 credentials, err := ReadCredentialsFile(JujuCredentialsPath()) 790 if err != nil { 791 return errors.Annotate(err, "cannot get credentials") 792 } 793 794 credentials.UpdateCloudCredential(cloudName, details) 795 return WriteCredentialsFile(credentials) 796 } 797 798 // CredentialForCloud implements CredentialGetter. 799 func (s *store) CredentialForCloud(cloudName string) (*cloud.CloudCredential, error) { 800 credentialCollection, err := ReadCredentialsFile(JujuCredentialsPath()) 801 if err != nil { 802 return nil, errors.Trace(err) 803 } 804 credential, err := credentialCollection.CloudCredential(cloudName) 805 if err != nil { 806 return nil, errors.Trace(err) 807 } 808 return credential, nil 809 } 810 811 // AllCredentials implements CredentialGetter. 812 func (s *store) AllCredentials() (map[string]cloud.CloudCredential, error) { 813 credentialCollection, err := ReadCredentialsFile(JujuCredentialsPath()) 814 if err != nil { 815 return nil, errors.Trace(err) 816 } 817 cloudNames := credentialCollection.CloudNames() 818 cloudCredentials := make(map[string]cloud.CloudCredential) 819 for _, cloud := range cloudNames { 820 v, err := credentialCollection.CloudCredential(cloud) 821 if err != nil { 822 return nil, errors.Trace(err) 823 } 824 cloudCredentials[cloud] = *v 825 } 826 return cloudCredentials, nil 827 } 828 829 // UpdateBootstrapConfig implements BootstrapConfigUpdater. 830 func (s *store) UpdateBootstrapConfig(controllerName string, cfg BootstrapConfig) error { 831 if err := ValidateControllerName(controllerName); err != nil { 832 return errors.Trace(err) 833 } 834 if err := ValidateBootstrapConfig(cfg); err != nil { 835 return errors.Trace(err) 836 } 837 838 releaser, err := s.acquireLock() 839 if err != nil { 840 return errors.Annotatef(err, 841 "cannot acquire lock file for updating the bootstrap config for controller %s", controllerName, 842 ) 843 } 844 defer releaser.Release() 845 846 all, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath()) 847 if err != nil { 848 return errors.Annotate(err, "cannot get bootstrap config") 849 } 850 851 if all == nil { 852 all = make(map[string]BootstrapConfig) 853 } 854 all[controllerName] = cfg 855 return WriteBootstrapConfigFile(all) 856 } 857 858 // BootstrapConfigForController implements BootstrapConfigGetter. 859 func (s *store) BootstrapConfigForController(controllerName string) (*BootstrapConfig, error) { 860 configs, err := ReadBootstrapConfigFile(JujuBootstrapConfigPath()) 861 if err != nil { 862 return nil, errors.Trace(err) 863 } 864 cfg, ok := configs[controllerName] 865 if !ok { 866 return nil, errors.NotFoundf("bootstrap config for controller %s", controllerName) 867 } 868 return &cfg, nil 869 } 870 871 // CookieJar returns the cookie jar associated with the given controller. 872 func (s *store) CookieJar(controllerName string) (CookieJar, error) { 873 if err := ValidateControllerName(controllerName); err != nil { 874 return nil, errors.Trace(err) 875 } 876 path := JujuCookiePath(controllerName) 877 jar, err := cookiejar.New(&cookiejar.Options{ 878 Filename: path, 879 }) 880 if err != nil { 881 return nil, errors.Trace(err) 882 } 883 return &cookieJar{ 884 path: path, 885 Jar: jar, 886 }, nil 887 } 888 889 type cookieJar struct { 890 path string 891 *cookiejar.Jar 892 } 893 894 func (jar *cookieJar) Save() error { 895 // Ensure that the directory exists before saving. 896 if err := os.MkdirAll(filepath.Dir(jar.path), 0700); err != nil { 897 return errors.Annotatef(err, "cannot make cookies directory") 898 } 899 return jar.Jar.Save() 900 } 901 902 // JujuCookiePath is the location where cookies associated 903 // with the given controller are expected to be found. 904 func JujuCookiePath(controllerName string) string { 905 return osenv.JujuXDGDataHomePath("cookies", controllerName+".json") 906 }