github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/jujuclient/accounts.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package jujuclient 5 6 import ( 7 "os" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/names/v5" 12 "github.com/juju/utils/v3" 13 "gopkg.in/yaml.v2" 14 15 "github.com/juju/juju/juju/osenv" 16 ) 17 18 // JujuAccountsPath is the location where accounts information is 19 // expected to be found. 20 func JujuAccountsPath() string { 21 return osenv.JujuXDGDataHomePath("accounts.yaml") 22 } 23 24 // ReadAccountsFile loads all accounts defined in a given file. 25 // If the file is not found, it is not an error. 26 func ReadAccountsFile(file string) (map[string]AccountDetails, error) { 27 data, err := os.ReadFile(file) 28 if err != nil { 29 if os.IsNotExist(err) { 30 return nil, nil 31 } 32 return nil, err 33 } 34 accounts, err := ParseAccounts(data) 35 if err != nil { 36 return nil, err 37 } 38 if err := migrateLocalAccountUsers(accounts); err != nil { 39 return nil, err 40 } 41 return accounts, nil 42 } 43 44 func migrateLocalAccountUsers(accounts map[string]AccountDetails) error { 45 changes := false 46 for user, account := range accounts { 47 if !strings.HasSuffix(account.User, "@local") { 48 continue 49 } 50 tag := names.NewUserTag(account.User) 51 updated := account 52 updated.User = tag.Id() 53 accounts[user] = updated 54 changes = true 55 } 56 if changes { 57 return WriteAccountsFile(accounts) 58 } 59 return nil 60 } 61 62 // WriteAccountsFile marshals to YAML details of the given accounts 63 // and writes it to the accounts file. 64 func WriteAccountsFile(controllerAccounts map[string]AccountDetails) error { 65 data, err := yaml.Marshal(accountsCollection{controllerAccounts}) 66 if err != nil { 67 return errors.Annotate(err, "cannot marshal accounts") 68 } 69 return utils.AtomicWriteFile(JujuAccountsPath(), data, os.FileMode(0600)) 70 } 71 72 // ParseAccounts parses the given YAML bytes into accounts metadata. 73 func ParseAccounts(data []byte) (map[string]AccountDetails, error) { 74 var result accountsCollection 75 if err := yaml.Unmarshal(data, &result); err != nil { 76 return nil, errors.Annotate(err, "cannot unmarshal accounts") 77 } 78 return result.ControllerAccounts, nil 79 } 80 81 type accountsCollection struct { 82 ControllerAccounts map[string]AccountDetails `yaml:"controllers"` 83 }