1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package machineenvironmentworker
11
"github.com/juju/loggo"
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"
25
logger = loggo.GetLogger("juju.worker.machineenvironment")
27
// ProxyDirectory is the directory containing the proxy file that contains
28
// the environment settings for the proxies based on the environment
30
ProxyDirectory = "/home/ubuntu"
32
// ProxyFile is the name of the file to be stored in the ProxyDirectory.
33
ProxyFile = ".juju-proxy"
35
// Started is a function that is called when the worker has started.
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
44
type MachineEnvironmentWorker struct {
45
api *environment.Facade
46
aptProxy osenv.ProxySettings
47
proxy osenv.ProxySettings
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
61
var _ worker.NotifyWatchHandler = (*MachineEnvironmentWorker)(nil)
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{
73
writeSystemFiles: writeSystemFiles,
76
return worker.NewNotifyWorker(envWorker)
79
func (w *MachineEnvironmentWorker) writeEnvironmentFile() error {
80
// Writing the environment file is handled by executing the script for two
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.
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.
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
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)`,
101
utils.ShQuote(w.proxy.AsScriptEnvironment()),
103
WorkingDir: ProxyDirectory,
108
if result.Code != 0 {
109
logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
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)
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)
136
// It isn't really fatal, but we should record it.
137
logger.Errorf("error writing apt proxy config file: %v", err)
142
func (w *MachineEnvironmentWorker) onChange() error {
143
env, err := w.api.EnvironConfig()
147
w.handleProxyValues(env.ProxySettings())
148
w.handleAptProxyValues(env.AptProxySettings())
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
162
return w.api.WatchForEnvironConfigChanges()
165
// Handle is defined on the worker.NotifyWatchHandler interface.
166
func (w *MachineEnvironmentWorker) Handle() error {
170
// TearDown is defined on the worker.NotifyWatchHandler interface.
171
func (w *MachineEnvironmentWorker) TearDown() error {
172
// Nothing to cleanup, only state is the watcher