launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/machineenvironmentworker/machineenvironmentworker.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package machineenvironmentworker 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "path" 10 11 "github.com/loggo/loggo" 12 13 "launchpad.net/errgo/errors" 14 "launchpad.net/juju-core/agent" 15 "launchpad.net/juju-core/juju/osenv" 16 "launchpad.net/juju-core/names" 17 "launchpad.net/juju-core/provider" 18 "launchpad.net/juju-core/state/api/environment" 19 "launchpad.net/juju-core/state/api/watcher" 20 "launchpad.net/juju-core/utils" 21 "launchpad.net/juju-core/utils/exec" 22 "launchpad.net/juju-core/worker" 23 ) 24 25 var ( 26 logger = loggo.GetLogger("juju.worker.machineenvironment") 27 28 mask = errors.Mask 29 30 // ProxyDirectory is the directory containing the proxy file that contains 31 // the environment settings for the proxies based on the environment 32 // config values. 33 ProxyDirectory = "/home/ubuntu" 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 // MachineEnvironmentWorker 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 MachineEnvironmentWorker struct { 48 api *environment.Facade 49 aptProxy osenv.ProxySettings 50 proxy osenv.ProxySettings 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 = (*MachineEnvironmentWorker)(nil) 65 66 // NewMachineEnvironmentWorker returns a worker.Worker that uses the notify 67 // watcher returned from the setup. 68 func NewMachineEnvironmentWorker(api *environment.Facade, agentConfig agent.Config) worker.Worker { 69 // We don't write out system files for the local provider on machine zero 70 // as that is the host machine. 71 writeSystemFiles := !(agentConfig.Tag() == names.MachineTag("0") && 72 agentConfig.Value(agent.JujuProviderType) == provider.Local) 73 envWorker := &MachineEnvironmentWorker{ 74 api: api, 75 writeSystemFiles: writeSystemFiles, 76 first: true, 77 } 78 return worker.NewNotifyWorker(envWorker) 79 } 80 81 func (w *MachineEnvironmentWorker) writeEnvironmentFile() error { 82 // Writing the environment file is handled by executing the script for two 83 // primary reasons: 84 // 85 // 1: In order to have the local provider specify the environment settings 86 // for the machine agent running on the host, this worker needs to run, 87 // but it shouldn't be touching any files on the disk. If however there is 88 // an ubuntu user, it will. This shouldn't be a problem. 89 // 90 // 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in 91 // the situation where the ubuntu user has been created as a part of the 92 // manual provisioning process, the user will exist, and will not have the 93 // same uid/gid as the default cloud image. 94 // 95 // It is easier to shell out to check both these things, and is also the 96 // same way that the file is written in the cloud-init process, so 97 // consistency FTW. 98 filePath := path.Join(ProxyDirectory, ProxyFile) 99 result, err := exec.RunCommands(exec.RunParams{ 100 Commands: fmt.Sprintf( 101 `[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`, 102 ProxyDirectory, 103 utils.ShQuote(w.proxy.AsScriptEnvironment()), 104 filePath, filePath), 105 WorkingDir: ProxyDirectory, 106 }) 107 if err != nil { 108 return mask(err) 109 } 110 if result.Code != 0 { 111 logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr) 112 } 113 return nil 114 } 115 116 func (w *MachineEnvironmentWorker) handleProxyValues(proxySettings osenv.ProxySettings) { 117 if proxySettings != w.proxy || w.first { 118 logger.Debugf("new proxy settings %#v", proxySettings) 119 w.proxy = proxySettings 120 w.proxy.SetEnvironmentValues() 121 if w.writeSystemFiles { 122 if err := w.writeEnvironmentFile(); err != nil { 123 // It isn't really fatal, but we should record it. 124 logger.Errorf("error writing proxy environment file: %v", err) 125 } 126 } 127 } 128 } 129 130 func (w *MachineEnvironmentWorker) handleAptProxyValues(aptSettings osenv.ProxySettings) { 131 if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) { 132 logger.Debugf("new apt proxy settings %#v", aptSettings) 133 w.aptProxy = aptSettings 134 // Always finish with a new line. 135 content := utils.AptProxyContent(w.aptProxy) + "\n" 136 err := ioutil.WriteFile(utils.AptConfFile, []byte(content), 0644) 137 if err != nil { 138 // It isn't really fatal, but we should record it. 139 logger.Errorf("error writing apt proxy config file: %v", err) 140 } 141 } 142 } 143 144 func (w *MachineEnvironmentWorker) onChange() error { 145 env, err := w.api.EnvironConfig() 146 if err != nil { 147 return mask(err) 148 } 149 w.handleProxyValues(env.ProxySettings()) 150 w.handleAptProxyValues(env.AptProxySettings()) 151 return nil 152 } 153 154 // SetUp is defined on the worker.NotifyWatchHandler interface. 155 func (w *MachineEnvironmentWorker) SetUp() (watcher.NotifyWatcher, error) { 156 // We need to set this up initially as the NotifyWorker sucks up the first 157 // event. 158 err := w.onChange() 159 if err != nil { 160 return nil, mask(err) 161 } 162 w.first = false 163 Started() 164 return w.api.WatchForEnvironConfigChanges() 165 } 166 167 // Handle is defined on the worker.NotifyWatchHandler interface. 168 func (w *MachineEnvironmentWorker) Handle() error { 169 return w.onChange() 170 } 171 172 // TearDown is defined on the worker.NotifyWatchHandler interface. 173 func (w *MachineEnvironmentWorker) TearDown() error { 174 // Nothing to cleanup, only state is the watcher 175 return nil 176 }