1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/errors"
11
"github.com/juju/utils"
14
"github.com/juju/juju/juju/osenv"
17
// JujuAccountsPath is the location where accounts information is
18
// expected to be found.
19
func JujuAccountsPath() string {
20
return osenv.JujuXDGDataHomePath("accounts.yaml")
23
// ReadAccountsFile loads all accounts defined in a given file.
24
// If the file is not found, it is not an error.
25
func ReadAccountsFile(file string) (map[string]AccountDetails, error) {
26
data, err := ioutil.ReadFile(file)
28
if os.IsNotExist(err) {
33
if err := migrateLegacyAccounts(data); err != nil {
36
accounts, err := ParseAccounts(data)
43
// WriteAccountsFile marshals to YAML details of the given accounts
44
// and writes it to the accounts file.
45
func WriteAccountsFile(controllerAccounts map[string]AccountDetails) error {
46
data, err := yaml.Marshal(accountsCollection{controllerAccounts})
48
return errors.Annotate(err, "cannot marshal accounts")
50
return utils.AtomicWriteFile(JujuAccountsPath(), data, os.FileMode(0600))
53
// ParseAccounts parses the given YAML bytes into accounts metadata.
54
func ParseAccounts(data []byte) (map[string]AccountDetails, error) {
55
var result accountsCollection
56
if err := yaml.Unmarshal(data, &result); err != nil {
57
return nil, errors.Annotate(err, "cannot unmarshal accounts")
59
return result.ControllerAccounts, nil
62
type accountsCollection struct {
63
ControllerAccounts map[string]AccountDetails `yaml:"controllers"`
66
// TODO(axw) 2016-07-14 #1603841
67
// Drop this code once we get to 2.0.
68
func migrateLegacyAccounts(data []byte) error {
69
type legacyControllerAccounts struct {
70
Accounts map[string]AccountDetails `yaml:"accounts"`
71
CurrentAccount string `yaml:"current-account,omitempty"`
73
type legacyAccountsCollection struct {
74
ControllerAccounts map[string]legacyControllerAccounts `yaml:"controllers"`
76
var legacy legacyAccountsCollection
77
if err := yaml.Unmarshal(data, &legacy); err != nil {
78
return errors.Annotate(err, "cannot unmarshal accounts")
80
result := make(map[string]AccountDetails)
81
for controller, controllerAccounts := range legacy.ControllerAccounts {
82
if controllerAccounts.CurrentAccount == "" {
85
details, ok := controllerAccounts.Accounts[controllerAccounts.CurrentAccount]
89
result[controller] = details
92
// Only write if we found at least one,
93
// which means the file was in legacy
94
// format. Otherwise leave it alone.
95
return WriteAccountsFile(result)