github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/envcmd/files.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package envcmd 5 6 import ( 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 "time" 12 13 "github.com/juju/cmd" 14 "github.com/juju/errors" 15 "github.com/juju/utils/fslock" 16 17 "github.com/juju/juju/juju/osenv" 18 ) 19 20 const ( 21 CurrentEnvironmentFilename = "current-environment" 22 CurrentSystemFilename = "current-system" 23 24 lockName = "current.lock" 25 26 systemSuffix = " (system)" 27 ) 28 29 var ( 30 // 5 seconds should be way more than enough to write or read any files 31 // even on heavily loaded systems. 32 lockTimeout = 5 * time.Second 33 ) 34 35 // ServerFile describes the information that is needed for a user 36 // to connect to an api server. 37 type ServerFile struct { 38 Addresses []string `yaml:"addresses"` 39 CACert string `yaml:"ca-cert,omitempty"` 40 Username string `yaml:"username"` 41 Password string `yaml:"password"` 42 } 43 44 // NOTE: synchronisation across functions in this file. 45 // 46 // Each of the read and write functions use a fslock to synchronise calls 47 // across both the current executable and across different executables. 48 49 func getCurrentEnvironmentFilePath() string { 50 return filepath.Join(osenv.JujuHome(), CurrentEnvironmentFilename) 51 } 52 53 func getCurrentSystemFilePath() string { 54 return filepath.Join(osenv.JujuHome(), CurrentSystemFilename) 55 } 56 57 // Read the file $JUJU_HOME/current-environment and return the value stored 58 // there. If the file doesn't exist an empty string is returned and no error. 59 func ReadCurrentEnvironment() (string, error) { 60 lock, err := acquireEnvironmentLock("read current-environment") 61 if err != nil { 62 return "", errors.Trace(err) 63 } 64 defer lock.Unlock() 65 66 current, err := ioutil.ReadFile(getCurrentEnvironmentFilePath()) 67 if err != nil { 68 if os.IsNotExist(err) { 69 return "", nil 70 } 71 return "", errors.Trace(err) 72 } 73 return strings.TrimSpace(string(current)), nil 74 } 75 76 // Read the file $JUJU_HOME/current-system and return the value stored there. 77 // If the file doesn't exist an empty string is returned and no error. 78 func ReadCurrentSystem() (string, error) { 79 lock, err := acquireEnvironmentLock("read current-system") 80 if err != nil { 81 return "", errors.Trace(err) 82 } 83 defer lock.Unlock() 84 85 current, err := ioutil.ReadFile(getCurrentSystemFilePath()) 86 if err != nil { 87 if os.IsNotExist(err) { 88 return "", nil 89 } 90 return "", errors.Trace(err) 91 } 92 return strings.TrimSpace(string(current)), nil 93 } 94 95 // Write the envName to the file $JUJU_HOME/current-environment file. 96 func WriteCurrentEnvironment(envName string) error { 97 lock, err := acquireEnvironmentLock("write current-environment") 98 if err != nil { 99 return errors.Trace(err) 100 } 101 defer lock.Unlock() 102 103 path := getCurrentEnvironmentFilePath() 104 err = ioutil.WriteFile(path, []byte(envName+"\n"), 0644) 105 if err != nil { 106 return errors.Errorf("unable to write to the environment file: %q, %s", path, err) 107 } 108 // If there is a current system file, remove it. 109 if err := os.Remove(getCurrentSystemFilePath()); err != nil && !os.IsNotExist(err) { 110 logger.Debugf("removing the current environment file due to %s", err) 111 // Best attempt to remove the file we just wrote. 112 os.Remove(path) 113 return err 114 } 115 return nil 116 } 117 118 // Write the systemName to the file $JUJU_HOME/current-system file. 119 func WriteCurrentSystem(systemName string) error { 120 lock, err := acquireEnvironmentLock("write current-system") 121 if err != nil { 122 return errors.Trace(err) 123 } 124 defer lock.Unlock() 125 126 path := getCurrentSystemFilePath() 127 err = ioutil.WriteFile(path, []byte(systemName+"\n"), 0644) 128 if err != nil { 129 return errors.Errorf("unable to write to the system file: %q, %s", path, err) 130 } 131 // If there is a current environment file, remove it. 132 if err := os.Remove(getCurrentEnvironmentFilePath()); err != nil && !os.IsNotExist(err) { 133 logger.Debugf("removing the current system file due to %s", err) 134 // Best attempt to remove the file we just wrote. 135 os.Remove(path) 136 return err 137 } 138 return nil 139 } 140 141 func acquireEnvironmentLock(operation string) (*fslock.Lock, error) { 142 // NOTE: any reading or writing from the directory should be done with a 143 // fslock to make sure we have a consistent read or write. Also worth 144 // noting, we should use a very short timeout. 145 lock, err := fslock.NewLock(osenv.JujuHome(), lockName) 146 if err != nil { 147 return nil, errors.Trace(err) 148 } 149 err = lock.LockWithTimeout(lockTimeout, operation) 150 if err != nil { 151 return nil, errors.Trace(err) 152 } 153 return lock, nil 154 } 155 156 // CurrentConnectionName looks at both the current environment file 157 // and the current system file to determine which is active. 158 // The name of the current environment or system is returned along with 159 // a boolean to express whether the name refers to a system or environment. 160 func CurrentConnectionName() (name string, is_system bool, err error) { 161 currentEnv, err := ReadCurrentEnvironment() 162 if err != nil { 163 return "", false, errors.Trace(err) 164 } else if currentEnv != "" { 165 return currentEnv, false, nil 166 } 167 168 currentSystem, err := ReadCurrentSystem() 169 if err != nil { 170 return "", false, errors.Trace(err) 171 } else if currentSystem != "" { 172 return currentSystem, true, nil 173 } 174 175 return "", false, nil 176 } 177 178 func currentName() (string, error) { 179 name, isSystem, err := CurrentConnectionName() 180 if err != nil { 181 return "", errors.Trace(err) 182 } 183 if isSystem { 184 name = name + systemSuffix 185 } 186 if name != "" { 187 name += " " 188 } 189 return name, nil 190 } 191 192 // SetCurrentEnvironment writes out the current environment file and writes a 193 // standard message to the command context. 194 func SetCurrentEnvironment(context *cmd.Context, environmentName string) error { 195 current, err := currentName() 196 if err != nil { 197 return errors.Trace(err) 198 } 199 err = WriteCurrentEnvironment(environmentName) 200 if err != nil { 201 return errors.Trace(err) 202 } 203 context.Infof("%s-> %s", current, environmentName) 204 return nil 205 } 206 207 // SetCurrentSystem writes out the current system file and writes a standard 208 // message to the command context. 209 func SetCurrentSystem(context *cmd.Context, systemName string) error { 210 current, err := currentName() 211 if err != nil { 212 return errors.Trace(err) 213 } 214 err = WriteCurrentSystem(systemName) 215 if err != nil { 216 return errors.Trace(err) 217 } 218 context.Infof("%s-> %s%s", current, systemName, systemSuffix) 219 return nil 220 }