github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/proxyupdater/proxyupdater.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package proxyupdater 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "path" 10 11 "github.com/juju/loggo" 12 "github.com/juju/utils" 13 "github.com/juju/utils/exec" 14 "github.com/juju/utils/packaging/commands" 15 "github.com/juju/utils/packaging/config" 16 proxyutils "github.com/juju/utils/proxy" 17 18 "github.com/juju/juju/api/environment" 19 "github.com/juju/juju/api/watcher" 20 "github.com/juju/juju/version" 21 "github.com/juju/juju/worker" 22 ) 23 24 var ( 25 logger = loggo.GetLogger("juju.worker.proxyupdater") 26 27 // ProxyDirectory is the directory containing the proxy file that contains 28 // the environment settings for the proxies based on the environment 29 // config values. 30 ProxyDirectory = "/home/ubuntu" 31 32 // proxySettingsRegistryPath is the Windows registry path where the proxy settings are saved 33 proxySettingsRegistryPath = `HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings` 34 35 // ProxyFile is the name of the file to be stored in the ProxyDirectory. 36 ProxyFile = ".juju-proxy" 37 38 // Started is a function that is called when the worker has started. 39 Started = func() {} 40 ) 41 42 // proxyWorker is responsible for monitoring the juju environment 43 // configuration and making changes on the physical (or virtual) machine as 44 // necessary to match the environment changes. Examples of these types of 45 // changes are apt proxy configuration and the juju proxies stored in the juju 46 // proxy file. 47 type proxyWorker struct { 48 api *environment.Facade 49 aptProxy proxyutils.Settings 50 proxy proxyutils.Settings 51 52 writeSystemFiles bool 53 // The whole point of the first value is to make sure that the the files 54 // are written out the first time through, even if they are the same as 55 // "last" time, as the initial value for last time is the zeroed struct. 56 // There is the possibility that the files exist on disk with old 57 // settings, and the environment has been updated to now not have them. We 58 // need to make sure that the disk reflects the environment, so the first 59 // time through, even if the proxies are empty, we write the files to 60 // disk. 61 first bool 62 } 63 64 var _ worker.NotifyWatchHandler = (*proxyWorker)(nil) 65 66 // New returns a worker.Worker that updates proxy environment variables for the 67 // process; and, if writeSystemFiles is true, for the whole machine. 68 var New = func(api *environment.Facade, writeSystemFiles bool) worker.Worker { 69 logger.Debugf("write system files: %v", writeSystemFiles) 70 envWorker := &proxyWorker{ 71 api: api, 72 writeSystemFiles: writeSystemFiles, 73 first: true, 74 } 75 return worker.NewNotifyWorker(envWorker) 76 } 77 78 func (w *proxyWorker) writeEnvironmentFile() error { 79 // Writing the environment file is handled by executing the script for two 80 // primary reasons: 81 // 82 // 1: In order to have the local provider specify the environment settings 83 // for the machine agent running on the host, this worker needs to run, 84 // but it shouldn't be touching any files on the disk. If however there is 85 // an ubuntu user, it will. This shouldn't be a problem. 86 // 87 // 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in 88 // the situation where the ubuntu user has been created as a part of the 89 // manual provisioning process, the user will exist, and will not have the 90 // same uid/gid as the default cloud image. 91 // 92 // It is easier to shell out to check both these things, and is also the 93 // same way that the file is written in the cloud-init process, so 94 // consistency FTW. 95 filePath := path.Join(ProxyDirectory, ProxyFile) 96 result, err := exec.RunCommands(exec.RunParams{ 97 Commands: fmt.Sprintf( 98 `[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`, 99 ProxyDirectory, 100 utils.ShQuote(w.proxy.AsScriptEnvironment()), 101 filePath, filePath), 102 WorkingDir: ProxyDirectory, 103 }) 104 if err != nil { 105 return err 106 } 107 if result.Code != 0 { 108 logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr) 109 } 110 return nil 111 } 112 113 func (w *proxyWorker) writeEnvironmentToRegistry() error { 114 // On windows we write the proxy settings to the registry. 115 setProxyScript := `$value_path = "%s" 116 $new_proxy = "%s" 117 $proxy_val = Get-ItemProperty -Path $value_path -Name ProxySettings 118 if ($? -eq $false){ New-ItemProperty -Path $value_path -Name ProxySettings -PropertyType String -Value $new_proxy }else{ Set-ItemProperty -Path $value_path -Name ProxySettings -Value $new_proxy } 119 ` 120 result, err := exec.RunCommands(exec.RunParams{ 121 Commands: fmt.Sprintf( 122 setProxyScript, 123 proxySettingsRegistryPath, 124 w.proxy.Http), 125 }) 126 if err != nil { 127 return err 128 } 129 if result.Code != 0 { 130 logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr) 131 } 132 return nil 133 } 134 135 func (w *proxyWorker) writeEnvironment() error { 136 osystem, err := version.GetOSFromSeries(version.Current.Series) 137 if err != nil { 138 return err 139 } 140 switch osystem { 141 case version.Windows: 142 return w.writeEnvironmentToRegistry() 143 default: 144 return w.writeEnvironmentFile() 145 } 146 } 147 148 func (w *proxyWorker) handleProxyValues(proxySettings proxyutils.Settings) { 149 proxySettings.SetEnvironmentValues() 150 if proxySettings != w.proxy || w.first { 151 logger.Debugf("new proxy settings %#v", proxySettings) 152 w.proxy = proxySettings 153 if w.writeSystemFiles { 154 if err := w.writeEnvironment(); err != nil { 155 // It isn't really fatal, but we should record it. 156 logger.Errorf("error writing proxy environment file: %v", err) 157 } 158 } 159 } 160 } 161 162 // getPackageCommander is a helper function which returns the 163 // package commands implementation for the current system. 164 func getPackageCommander() (commands.PackageCommander, error) { 165 return commands.NewPackageCommander(version.Current.Series) 166 } 167 168 func (w *proxyWorker) handleAptProxyValues(aptSettings proxyutils.Settings) error { 169 if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) { 170 logger.Debugf("new apt proxy settings %#v", aptSettings) 171 paccmder, err := getPackageCommander() 172 if err != nil { 173 return err 174 } 175 w.aptProxy = aptSettings 176 177 // Always finish with a new line. 178 content := paccmder.ProxyConfigContents(w.aptProxy) + "\n" 179 err = ioutil.WriteFile(config.AptProxyConfigFile, []byte(content), 0644) 180 if err != nil { 181 // It isn't really fatal, but we should record it. 182 logger.Errorf("error writing apt proxy config file: %v", err) 183 } 184 } 185 return nil 186 } 187 188 func (w *proxyWorker) onChange() error { 189 env, err := w.api.EnvironConfig() 190 if err != nil { 191 return err 192 } 193 w.handleProxyValues(env.ProxySettings()) 194 err = w.handleAptProxyValues(env.AptProxySettings()) 195 if err != nil { 196 return err 197 } 198 return nil 199 } 200 201 // SetUp is defined on the worker.NotifyWatchHandler interface. 202 func (w *proxyWorker) SetUp() (watcher.NotifyWatcher, error) { 203 // We need to set this up initially as the NotifyWorker sucks up the first 204 // event. 205 err := w.onChange() 206 if err != nil { 207 return nil, err 208 } 209 w.first = false 210 Started() 211 return w.api.WatchForEnvironConfigChanges() 212 } 213 214 // Handle is defined on the worker.NotifyWatchHandler interface. 215 func (w *proxyWorker) Handle(_ <-chan struct{}) error { 216 return w.onChange() 217 } 218 219 // TearDown is defined on the worker.NotifyWatchHandler interface. 220 func (w *proxyWorker) TearDown() error { 221 // Nothing to cleanup, only state is the watcher 222 return nil 223 }