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