Skip to content

Commit 92234a6

Browse files
Add windows userdata support
* refactor cloudinit packages to include support for windows userdata * fix NewMachineConfig to store corect paths when on windows
1 parent 0f96d48 commit 92234a6

40 files changed

+871
-352
lines changed

agent/agent.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"github.com/juju/names"
2121
"github.com/juju/utils"
2222

23+
"github.com/juju/juju/cloudinit"
2324
"github.com/juju/juju/environmentserver/authentication"
25+
"github.com/juju/juju/juju/paths"
2426
"github.com/juju/juju/mongo"
2527
"github.com/juju/juju/network"
2628
"github.com/juju/juju/state/api"
@@ -32,16 +34,10 @@ var logger = loggo.GetLogger("juju.agent")
3234

3335
// logDir returns a filesystem path to the location where juju
3436
// may create a folder containing its logs
35-
//
36-
// TODO(gsamfira) 2014-07-31 https://github.com/juju/juju/pull/189
37-
// Use the target series to decide the path.
38-
var logDir = "/var/log"
37+
var logDir = paths.MustSucceed(paths.LogDir(version.Current.Series))
3938

4039
// dataDir returns the default data directory for this running system
41-
//
42-
// TODO(gsamfira) 2014-07-31 https://github.com/juju/juju/pull/189
43-
// Use the target series to decide the path.
44-
var dataDir = "/var/lib/juju"
40+
var dataDir = paths.MustSucceed(paths.DataDir(version.Current.Series))
4541

4642
// DefaultLogDir defines the default log directory for juju agents.
4743
// It's defined as a variable so it could be overridden in tests.
@@ -112,7 +108,7 @@ type Config interface {
112108
// WriteCommands returns shell commands to write the agent configuration.
113109
// It returns an error if the configuration does not have all the right
114110
// elements.
115-
WriteCommands() ([]string, error)
111+
WriteCommands(series string) ([]string, error)
116112

117113
// StateServingInfo returns the details needed to run
118114
// a state server and reports whether those details
@@ -627,13 +623,17 @@ func (c *configInternal) fileContents() ([]byte, error) {
627623
return buf.Bytes(), nil
628624
}
629625

630-
func (c *configInternal) WriteCommands() ([]string, error) {
626+
func (c *configInternal) WriteCommands(series string) ([]string, error) {
627+
renderer, err := cloudinit.NewRenderer(series)
628+
if err != nil {
629+
return nil, err
630+
}
631631
data, err := c.fileContents()
632632
if err != nil {
633633
return nil, err
634634
}
635-
commands := []string{"mkdir -p " + utils.ShQuote(c.Dir())}
636-
commands = append(commands, writeFileCommands(c.File(agentConfigFilename), data, 0600)...)
635+
commands := renderer.Mkdir(c.Dir())
636+
commands = append(commands, renderer.WriteFile(c.File(agentConfigFilename), string(data), 0600)...)
637637
return commands, nil
638638
}
639639

agent/format_whitebox_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func newTestConfig(c *gc.C) *configInternal {
4747

4848
func (*formatSuite) TestWriteCommands(c *gc.C) {
4949
config := newTestConfig(c)
50-
commands, err := config.WriteCommands()
50+
commands, err := config.WriteCommands("quantal")
5151
c.Assert(err, gc.IsNil)
5252
c.Assert(commands, gc.HasLen, 3)
5353
c.Assert(commands[0], gc.Matches, `mkdir -p '\S+/agents/machine-1'`)

cloudinit/cloudinit.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ package cloudinit
99
import (
1010
"bytes"
1111
"text/template"
12-
13-
"gopkg.in/yaml.v1"
1412
)
1513

1614
// Config represents a set of cloud-init configuration options.
@@ -23,15 +21,6 @@ func New() *Config {
2321
return &Config{make(map[string]interface{})}
2422
}
2523

26-
// Render returns the cloud-init configuration as a YAML file.
27-
func (cfg *Config) Render() ([]byte, error) {
28-
data, err := yaml.Marshal(cfg.attrs)
29-
if err != nil {
30-
return nil, err
31-
}
32-
return append([]byte("#cloud-config\n"), data...), nil
33-
}
34-
3524
func (cfg *Config) set(opt string, yes bool, value interface{}) {
3625
if yes {
3726
cfg.attrs[opt] = value

cloudinit/cloudinit_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ func (S) TestOutput(c *gc.C) {
295295
for _, t := range ctests {
296296
cfg := cloudinit.New()
297297
t.setOption(cfg)
298-
data, err := cfg.Render()
298+
renderer, err := cloudinit.NewRenderer("quantal")
299+
c.Assert(err, gc.IsNil)
300+
data, err := renderer.Render(cfg)
299301
c.Assert(err, gc.IsNil)
300302
c.Assert(data, gc.NotNil)
301303
c.Assert(string(data), gc.Equals, header+t.expect, gc.Commentf("test %q output differs", t.name))
@@ -364,7 +366,12 @@ func ExampleConfig() {
364366
cfg := cloudinit.New()
365367
cfg.AddPackage("juju")
366368
cfg.AddPackage("ubuntu")
367-
data, err := cfg.Render()
369+
renderer, err := cloudinit.NewRenderer("quantal")
370+
if err != nil {
371+
fmt.Printf("render error: %v", err)
372+
return
373+
}
374+
data, err := renderer.Render(cfg)
368375
if err != nil {
369376
fmt.Printf("render error: %v", err)
370377
return

cloudinit/interface.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package cloudinit
2+
3+
import (
4+
"github.com/juju/errors"
5+
6+
"github.com/juju/juju/version"
7+
)
8+
9+
type Renderer interface {
10+
// Mkdir returns an OS specific script for creating a directory
11+
Mkdir(path string) []string
12+
// WriteFile returns a command to write data
13+
WriteFile(filename string, contents string, permission int) []string
14+
// Render renders the userdata script for a particula OS type
15+
Render(conf *Config) ([]byte, error)
16+
// FromSlash returns the result of replacing each slash ('/') character
17+
// in path with a separator character. Multiple slashes are replaced by
18+
// multiple separators.
19+
FromSlash(path string) string
20+
// PathJoin will join a path using OS specific path separator.
21+
// This works for selected OS instead of current OS
22+
PathJoin(path ...string) string
23+
}
24+
25+
// NewRenderer returns a Renderer interface for selected series
26+
func NewRenderer(series string) (Renderer, error) {
27+
operatingSystem, err := version.GetOSFromSeries(series)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
switch operatingSystem {
33+
case version.Windows:
34+
return &WindowsRenderer{}, nil
35+
case version.Ubuntu:
36+
return &UbuntuRenderer{}, nil
37+
default:
38+
return nil, errors.Errorf("No renderer could be found for %s", series)
39+
}
40+
}

cloudinit/renderers.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package cloudinit
2+
3+
import (
4+
"fmt"
5+
"path"
6+
"strings"
7+
8+
"github.com/juju/utils"
9+
yaml "launchpad.net/goyaml"
10+
)
11+
12+
// UbuntuRenderer represents an Ubuntu specific script render
13+
// type that is responsible for this particular OS. It implements
14+
// the Renderer interface
15+
type UbuntuRenderer struct{}
16+
17+
func (w *UbuntuRenderer) Mkdir(path string) []string {
18+
return []string{fmt.Sprintf(`mkdir -p %s`, utils.ShQuote(path))}
19+
}
20+
21+
func (w *UbuntuRenderer) WriteFile(filename string, contents string, permission int) []string {
22+
quotedFilename := utils.ShQuote(filename)
23+
quotedContents := utils.ShQuote(contents)
24+
return []string{
25+
fmt.Sprintf("install -m %o /dev/null %s", permission, quotedFilename),
26+
fmt.Sprintf(`printf '%%s\n' %s > %s`, quotedContents, quotedFilename),
27+
}
28+
}
29+
30+
func (w *UbuntuRenderer) FromSlash(filepath string) string {
31+
return filepath
32+
}
33+
34+
func (w *UbuntuRenderer) PathJoin(filepath ...string) string {
35+
return w.FromSlash(path.Join(filepath...))
36+
}
37+
38+
func (w *UbuntuRenderer) Render(conf *Config) ([]byte, error) {
39+
data, err := yaml.Marshal(conf.attrs)
40+
if err != nil {
41+
return nil, err
42+
}
43+
return append([]byte("#cloud-config\n"), data...), nil
44+
}
45+
46+
// WindowsRenderer represents a Windows specific script render
47+
// type that is responsible for this particular OS. It implements
48+
// the Renderer interface
49+
type WindowsRenderer struct{}
50+
51+
func (w *WindowsRenderer) Mkdir(path string) []string {
52+
return []string{fmt.Sprintf(`mkdir %s`, w.FromSlash(path))}
53+
}
54+
55+
func (w *WindowsRenderer) WriteFile(filename string, contents string, permission int) []string {
56+
return []string{
57+
fmt.Sprintf("Set-Content '%s' @\"\n%s\n\"@", filename, contents),
58+
}
59+
}
60+
61+
func (w *WindowsRenderer) PathJoin(filepath ...string) string {
62+
return w.FromSlash(path.Join(filepath...))
63+
}
64+
65+
func (w *WindowsRenderer) FromSlash(path string) string {
66+
return strings.Replace(path, "/", `\`, -1)
67+
}
68+
69+
func (w *WindowsRenderer) Render(conf *Config) ([]byte, error) {
70+
winCmds := conf.attrs["runcmd"]
71+
var script []byte
72+
newline := "\r\n"
73+
header := "#ps1_sysnative\r\n"
74+
script = append(script, header...)
75+
for _, value := range winCmds.([]*command) {
76+
script = append(script, newline...)
77+
script = append(script, value.literal...)
78+
79+
}
80+
return script, nil
81+
}

cloudinit/sshinit/configure_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,28 @@ func testConfig(c *gc.C, stateServer bool, vers version.Binary) *config.Config {
5454

5555
func (s *configureSuite) getCloudConfig(c *gc.C, stateServer bool, vers version.Binary) *cloudinit.Config {
5656
var mcfg *envcloudinit.MachineConfig
57+
var err error
5758
if stateServer {
58-
mcfg = environs.NewBootstrapMachineConfig("private-key")
59+
mcfg, err = environs.NewBootstrapMachineConfig("private-key", vers.Series)
60+
c.Assert(err, gc.IsNil)
5961
mcfg.InstanceId = "instance-id"
6062
mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits}
6163
} else {
62-
mcfg = environs.NewMachineConfig("0", "ya", imagemetadata.ReleasedStream, nil, nil, nil)
64+
mcfg, err = environs.NewMachineConfig("0", "ya", imagemetadata.ReleasedStream, vers.Series, nil, nil, nil)
65+
c.Assert(err, gc.IsNil)
6366
mcfg.Jobs = []params.MachineJob{params.JobHostUnits}
6467
}
6568
mcfg.Tools = &tools.Tools{
6669
Version: vers,
6770
URL: "file:///var/lib/juju/storage/" + envtools.StorageName(vers),
6871
}
6972
environConfig := testConfig(c, stateServer, vers)
70-
err := environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{})
73+
err = environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{})
7174
c.Assert(err, gc.IsNil)
7275
cloudcfg := cloudinit.New()
73-
err = envcloudinit.Configure(mcfg, cloudcfg)
76+
udata, err := envcloudinit.NewUserdataConfig(mcfg, cloudcfg)
77+
c.Assert(err, gc.IsNil)
78+
err = udata.Configure()
7479
c.Assert(err, gc.IsNil)
7580
return cloudcfg
7681
}

container/kvm/live_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,16 @@ func createContainer(c *gc.C, manager container.Manager, machineId string) insta
8585
machineNonce := "fake-nonce"
8686
stateInfo := jujutesting.FakeStateInfo(machineId)
8787
apiInfo := jujutesting.FakeAPIInfo(machineId)
88-
machineConfig := environs.NewMachineConfig(machineId, machineNonce, imagemetadata.ReleasedStream, nil, stateInfo, apiInfo)
88+
machineConfig, err := environs.NewMachineConfig(machineId, machineNonce, imagemetadata.ReleasedStream, "quantal", nil, stateInfo, apiInfo)
89+
c.Assert(err, gc.IsNil)
8990
network := container.BridgeNetworkConfig("virbr0")
9091

9192
machineConfig.Tools = &tools.Tools{
9293
Version: version.MustParseBinary("2.3.4-foo-bar"),
9394
URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
9495
}
9596
environConfig := dummyConfig(c)
96-
err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{})
97+
err = environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{})
9798
c.Assert(err, gc.IsNil)
9899

99100
inst, hardware, err := manager.CreateContainer(machineConfig, "precise", network)

container/lxc/clonetemplate.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/juju/utils/tailer"
1717
"launchpad.net/golxc"
1818

19-
coreCloudinit "github.com/juju/juju/cloudinit"
19+
corecloudinit "github.com/juju/juju/cloudinit"
2020
"github.com/juju/juju/container"
2121
"github.com/juju/juju/environs/cloudinit"
2222
)
@@ -53,7 +53,7 @@ func templateUserData(
5353
authorizedKeys string,
5454
aptProxy proxy.Settings,
5555
) ([]byte, error) {
56-
config := coreCloudinit.New()
56+
config := corecloudinit.New()
5757
config.AddScripts(
5858
"set -xe", // ensure we run all the scripts or abort.
5959
)
@@ -66,7 +66,12 @@ func templateUserData(
6666
utils.ShQuote(templateShutdownUpstartScript),
6767
templateShutdownUpstartFilename,
6868
))
69-
data, err := config.Render()
69+
70+
renderer, err := corecloudinit.NewRenderer(series)
71+
if err != nil {
72+
return nil, err
73+
}
74+
data, err := renderer.Render(config)
7075
if err != nil {
7176
return nil, err
7277
}

container/lxc/lxc_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ func (s *LxcSuite) TestCreateContainerEventsWithClone(c *gc.C) {
250250
s.PatchValue(&s.useClone, true)
251251
// The template containers are created with an upstart job that
252252
// stops them once cloud init has finished. We emulate that here.
253-
template := "juju-series-lxc-template"
253+
template := "juju-quantal-template"
254254
ch := s.ensureTemplateStopped(template)
255255
defer func() { <-ch }()
256256
manager := s.makeManager(c, "test")
@@ -264,14 +264,14 @@ func (s *LxcSuite) TestCreateContainerEventsWithClone(c *gc.C) {
264264
}
265265

266266
func (s *LxcSuite) createTemplate(c *gc.C) golxc.Container {
267-
name := "juju-series-lxc-template"
267+
name := "juju-quantal-template"
268268
ch := s.ensureTemplateStopped(name)
269269
defer func() { <-ch }()
270270
network := container.BridgeNetworkConfig("nic42")
271271
authorizedKeys := "authorized keys list"
272272
aptProxy := proxy.Settings{}
273273
template, err := lxc.EnsureCloneTemplate(
274-
"ext4", "series", network, authorizedKeys, aptProxy)
274+
"ext4", "quantal", network, authorizedKeys, aptProxy)
275275
c.Assert(err, gc.IsNil)
276276
c.Assert(template.Name(), gc.Equals, name)
277277
s.AssertEvent(c, <-s.events, mock.Created, name)
@@ -300,7 +300,7 @@ func (s *LxcSuite) TestCreateContainerEventsWithCloneExistingTemplate(c *gc.C) {
300300
instance := containertesting.CreateContainer(c, manager, "1")
301301
name := string(instance.Id())
302302
cloned := <-s.events
303-
s.AssertEvent(c, cloned, mock.Cloned, "juju-series-lxc-template")
303+
s.AssertEvent(c, cloned, mock.Cloned, "juju-quantal-template")
304304
c.Assert(cloned.Args, gc.IsNil)
305305
s.AssertEvent(c, <-s.events, mock.Started, name)
306306
}
@@ -313,7 +313,7 @@ func (s *LxcSuite) TestCreateContainerEventsWithCloneExistingTemplateAUFS(c *gc.
313313
instance := containertesting.CreateContainer(c, manager, "1")
314314
name := string(instance.Id())
315315
cloned := <-s.events
316-
s.AssertEvent(c, cloned, mock.Cloned, "juju-series-lxc-template")
316+
s.AssertEvent(c, cloned, mock.Cloned, "juju-quantal-template")
317317
c.Assert(cloned.Args, gc.DeepEquals, []string{"--snapshot", "--backingstore", "aufs"})
318318
s.AssertEvent(c, <-s.events, mock.Started, name)
319319
}

container/testing/common.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ func MockMachineConfig(machineId string) *cloudinit.MachineConfig {
2525

2626
stateInfo := jujutesting.FakeStateInfo(machineId)
2727
apiInfo := jujutesting.FakeAPIInfo(machineId)
28-
machineConfig := environs.NewMachineConfig(machineId, "fake-nonce", imagemetadata.ReleasedStream, nil, stateInfo, apiInfo)
28+
machineConfig, err := environs.NewMachineConfig(machineId, "fake-nonce", imagemetadata.ReleasedStream, "quantal", nil, stateInfo, apiInfo)
29+
c.Assert(err, gc.IsNil)
2930
machineConfig.Tools = &tools.Tools{
3031
Version: version.MustParseBinary("2.3.4-quantal-amd64"),
3132
URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
@@ -50,7 +51,7 @@ func CreateContainerWithMachineConfig(
5051
) instance.Instance {
5152

5253
network := container.BridgeNetworkConfig("nic42")
53-
inst, hardware, err := manager.CreateContainer(machineConfig, "series", network)
54+
inst, hardware, err := manager.CreateContainer(machineConfig, "quantal", network)
5455
c.Assert(err, gc.IsNil)
5556
c.Assert(hardware, gc.NotNil)
5657
c.Assert(hardware.String(), gc.Not(gc.Equals), "")

0 commit comments

Comments
 (0)