github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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/juju/loggo" 12 13 "launchpad.net/juju-core/agent" 14 "launchpad.net/juju-core/juju/osenv" 15 "launchpad.net/juju-core/names" 16 "launchpad.net/juju-core/provider" 17 "launchpad.net/juju-core/state/api/environment" 18 "launchpad.net/juju-core/state/api/watcher" 19 "launchpad.net/juju-core/utils" 20 "launchpad.net/juju-core/utils/exec" 21 "launchpad.net/juju-core/worker" 22 ) 23 24 var ( 25 logger = loggo.GetLogger("juju.worker.machineenvironment") 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 // ProxyFile is the name of the file to be stored in the ProxyDirectory. 33 ProxyFile = ".juju-proxy" 34 35 // Started is a function that is called when the worker has started. 36 Started = func() {} 37 ) 38 39 // MachineEnvironmentWorker is responsible for monitoring the juju environment 40 // configuration and making changes on the physical (or virtual) machine as 41 // necessary to match the environment changes. Examples of these types of 42 // changes are apt proxy configuration and the juju proxies stored in the juju 43 // proxy file. 44 type MachineEnvironmentWorker struct { 45 api *environment.Facade 46 aptProxy osenv.ProxySettings 47 proxy osenv.ProxySettings 48 49 writeSystemFiles bool 50 // The whole point of the first value is to make sure that the the files 51 // are written out the first time through, even if they are the same as 52 // "last" time, as the initial value for last time is the zeroed struct. 53 // There is the possibility that the files exist on disk with old 54 // settings, and the environment has been updated to now not have them. We 55 // need to make sure that the disk reflects the environment, so the first 56 // time through, even if the proxies are empty, we write the files to 57 // disk. 58 first bool 59 } 60 61 var _ worker.NotifyWatchHandler = (*MachineEnvironmentWorker)(nil) 62 63 // NewMachineEnvironmentWorker returns a worker.Worker that uses the notify 64 // watcher returned from the setup. 65 func NewMachineEnvironmentWorker(api *environment.Facade, agentConfig agent.Config) worker.Worker { 66 // We don't write out system files for the local provider on machine zero 67 // as that is the host machine. 68 writeSystemFiles := (agentConfig.Tag() != names.MachineTag("0") || 69 agentConfig.Value(agent.ProviderType) != provider.Local) 70 logger.Debugf("write system files: %v", writeSystemFiles) 71 envWorker := &MachineEnvironmentWorker{ 72 api: api, 73 writeSystemFiles: writeSystemFiles, 74 first: true, 75 } 76 return worker.NewNotifyWorker(envWorker) 77 } 78 79 func (w *MachineEnvironmentWorker) 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 *MachineEnvironmentWorker) handleProxyValues(proxySettings osenv.ProxySettings) { 115 if proxySettings != w.proxy || w.first { 116 logger.Debugf("new proxy settings %#v", proxySettings) 117 w.proxy = proxySettings 118 w.proxy.SetEnvironmentValues() 119 if w.writeSystemFiles { 120 if err := w.writeEnvironmentFile(); err != nil { 121 // It isn't really fatal, but we should record it. 122 logger.Errorf("error writing proxy environment file: %v", err) 123 } 124 } 125 } 126 } 127 128 func (w *MachineEnvironmentWorker) handleAptProxyValues(aptSettings osenv.ProxySettings) { 129 if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) { 130 logger.Debugf("new apt proxy settings %#v", aptSettings) 131 w.aptProxy = aptSettings 132 // Always finish with a new line. 133 content := utils.AptProxyContent(w.aptProxy) + "\n" 134 err := ioutil.WriteFile(utils.AptConfFile, []byte(content), 0644) 135 if err != nil { 136 // It isn't really fatal, but we should record it. 137 logger.Errorf("error writing apt proxy config file: %v", err) 138 } 139 } 140 } 141 142 func (w *MachineEnvironmentWorker) onChange() error { 143 env, err := w.api.EnvironConfig() 144 if err != nil { 145 return err 146 } 147 w.handleProxyValues(env.ProxySettings()) 148 w.handleAptProxyValues(env.AptProxySettings()) 149 return nil 150 } 151 152 // SetUp is defined on the worker.NotifyWatchHandler interface. 153 func (w *MachineEnvironmentWorker) SetUp() (watcher.NotifyWatcher, error) { 154 // We need to set this up initially as the NotifyWorker sucks up the first 155 // event. 156 err := w.onChange() 157 if err != nil { 158 return nil, err 159 } 160 w.first = false 161 Started() 162 return w.api.WatchForEnvironConfigChanges() 163 } 164 165 // Handle is defined on the worker.NotifyWatchHandler interface. 166 func (w *MachineEnvironmentWorker) Handle() error { 167 return w.onChange() 168 } 169 170 // TearDown is defined on the worker.NotifyWatchHandler interface. 171 func (w *MachineEnvironmentWorker) TearDown() error { 172 // Nothing to cleanup, only state is the watcher 173 return nil 174 }