~ubuntu-branches/ubuntu/trusty/juju-core/trusty-proposed

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/worker/machineenvironmentworker/machineenvironmentworker.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-02-03 09:22:46 UTC
  • mfrom: (1.1.17)
  • Revision ID: package-import@ubuntu.com-20140203092246-e03vg402vztzo4qa
Tags: 1.17.2-0ubuntu1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/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.JujuProviderType) == provider.Local)
 
70
        envWorker := &MachineEnvironmentWorker{
 
71
                api:              api,
 
72
                writeSystemFiles: writeSystemFiles,
 
73
                first:            true,
 
74
        }
 
75
        return worker.NewNotifyWorker(envWorker)
 
76
}
 
77
 
 
78
func (w *MachineEnvironmentWorker) writeEnvironmentFile() error {
 
79
        // Writing the environment file is handled by executing the script for two
 
80
        // primary reasons:
 
81
        //
 
82
        // 1: In order to have the local provider specify the environment settings
 
83
        // for the machine agent running on the host, this worker needs to run,
 
84
        // but it shouldn't be touching any files on the disk.  If however there is
 
85
        // an ubuntu user, it will. This shouldn't be a problem.
 
86
        //
 
87
        // 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in
 
88
        // the situation where the ubuntu user has been created as a part of the
 
89
        // manual provisioning process, the user will exist, and will not have the
 
90
        // same uid/gid as the default cloud image.
 
91
        //
 
92
        // It is easier to shell out to check both these things, and is also the
 
93
        // same way that the file is written in the cloud-init process, so
 
94
        // consistency FTW.
 
95
        filePath := path.Join(ProxyDirectory, ProxyFile)
 
96
        result, err := exec.RunCommands(exec.RunParams{
 
97
                Commands: fmt.Sprintf(
 
98
                        `[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`,
 
99
                        ProxyDirectory,
 
100
                        utils.ShQuote(w.proxy.AsScriptEnvironment()),
 
101
                        filePath, filePath),
 
102
                WorkingDir: ProxyDirectory,
 
103
        })
 
104
        if err != nil {
 
105
                return err
 
106
        }
 
107
        if result.Code != 0 {
 
108
                logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
 
109
        }
 
110
        return nil
 
111
}
 
112
 
 
113
func (w *MachineEnvironmentWorker) handleProxyValues(proxySettings osenv.ProxySettings) {
 
114
        if proxySettings != w.proxy || w.first {
 
115
                logger.Debugf("new proxy settings %#v", proxySettings)
 
116
                w.proxy = proxySettings
 
117
                w.proxy.SetEnvironmentValues()
 
118
                if w.writeSystemFiles {
 
119
                        if err := w.writeEnvironmentFile(); err != nil {
 
120
                                // It isn't really fatal, but we should record it.
 
121
                                logger.Errorf("error writing proxy environment file: %v", err)
 
122
                        }
 
123
                }
 
124
        }
 
125
}
 
126
 
 
127
func (w *MachineEnvironmentWorker) handleAptProxyValues(aptSettings osenv.ProxySettings) {
 
128
        if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) {
 
129
                logger.Debugf("new apt proxy settings %#v", aptSettings)
 
130
                w.aptProxy = aptSettings
 
131
                // Always finish with a new line.
 
132
                content := utils.AptProxyContent(w.aptProxy) + "\n"
 
133
                err := ioutil.WriteFile(utils.AptConfFile, []byte(content), 0644)
 
134
                if err != nil {
 
135
                        // It isn't really fatal, but we should record it.
 
136
                        logger.Errorf("error writing apt proxy config file: %v", err)
 
137
                }
 
138
        }
 
139
}
 
140
 
 
141
func (w *MachineEnvironmentWorker) onChange() error {
 
142
        env, err := w.api.EnvironConfig()
 
143
        if err != nil {
 
144
                return err
 
145
        }
 
146
        w.handleProxyValues(env.ProxySettings())
 
147
        w.handleAptProxyValues(env.AptProxySettings())
 
148
        return nil
 
149
}
 
150
 
 
151
// SetUp is defined on the worker.NotifyWatchHandler interface.
 
152
func (w *MachineEnvironmentWorker) SetUp() (watcher.NotifyWatcher, error) {
 
153
        // We need to set this up initially as the NotifyWorker sucks up the first
 
154
        // event.
 
155
        err := w.onChange()
 
156
        if err != nil {
 
157
                return nil, err
 
158
        }
 
159
        w.first = false
 
160
        Started()
 
161
        return w.api.WatchForEnvironConfigChanges()
 
162
}
 
163
 
 
164
// Handle is defined on the worker.NotifyWatchHandler interface.
 
165
func (w *MachineEnvironmentWorker) Handle() error {
 
166
        return w.onChange()
 
167
}
 
168
 
 
169
// TearDown is defined on the worker.NotifyWatchHandler interface.
 
170
func (w *MachineEnvironmentWorker) TearDown() error {
 
171
        // Nothing to cleanup, only state is the watcher
 
172
        return nil
 
173
}