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