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