1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
13
corecloudinit "launchpad.net/juju-core/cloudinit"
14
"launchpad.net/juju-core/constraints"
15
"launchpad.net/juju-core/environs"
16
"launchpad.net/juju-core/environs/cloudinit"
17
envtools "launchpad.net/juju-core/environs/tools"
18
"launchpad.net/juju-core/juju/osenv"
19
"launchpad.net/juju-core/provider"
20
"launchpad.net/juju-core/state"
21
"launchpad.net/juju-core/state/api"
22
"launchpad.net/juju-core/tools"
23
"launchpad.net/juju-core/utils"
26
type provisionMachineAgentArgs struct {
30
machine *state.Machine
39
// provisionMachineAgent connects to a machine over SSH,
40
// copies across the tools, and installs a machine agent.
41
func provisionMachineAgent(args provisionMachineAgentArgs) error {
42
script, err := provisionMachineAgentScript(args)
46
scriptBase64 := base64.StdEncoding.EncodeToString([]byte(script))
47
script = fmt.Sprintf(`F=$(mktemp); echo %s | base64 -d > $F; . $F`, scriptBase64)
50
"-t", // allocate a pseudo-tty
51
"--", fmt.Sprintf("sudo bash -c '%s'", script),
53
cmd := exec.Command("ssh", sshArgs...)
54
cmd.Stdout = os.Stdout
55
cmd.Stderr = os.Stderr
60
// provisionMachineAgentScript generates the script necessary
61
// to install a machine agent on the specified host.
62
func provisionMachineAgentScript(args provisionMachineAgentArgs) (string, error) {
66
tools, err = findMachineAgentTools(args.env, args.series, args.arch)
72
// We generate a cloud-init config, which we'll then pull the runcmds
73
// and prerequisite packages out of. Rather than generating a cloud-config,
74
// we'll just generate a shell script.
75
mcfg := environs.NewMachineConfig(args.machine.Id(), args.nonce, args.stateInfo, args.apiInfo)
76
if args.dataDir != "" {
77
mcfg.DataDir = args.dataDir
80
err := environs.FinishMachineConfig(mcfg, args.env.Config(), constraints.Value{})
84
mcfg.MachineEnvironment[osenv.JujuProviderType] = provider.Manual
85
cloudcfg := corecloudinit.New()
86
if cloudcfg, err = cloudinit.Configure(mcfg, cloudcfg); err != nil {
90
// TODO(axw): 2013-08-23 bug 1215777
91
// Carry out configuration for ssh-keys-per-user,
92
// machine-updates-authkeys, using cloud-init config.
94
// Convert runcmds to a series of shell commands.
95
script := []string{"#!/bin/sh"}
96
for _, cmd := range cloudcfg.RunCmds() {
97
switch cmd := cmd.(type) {
99
// Quote args, so shell meta-characters are not interpreted.
100
for i, arg := range cmd[1:] {
101
cmd[i] = utils.ShQuote(arg)
103
script = append(script, strings.Join(cmd, " "))
105
script = append(script, cmd)
107
return "", fmt.Errorf("unexpected runcmd type: %T", cmd)
111
// The first command is "set -xe", which we want to leave in place.
112
head := []string{script[0]}
114
for _, pkg := range cloudcfg.Packages() {
115
cmd := fmt.Sprintf("apt-get -y install %s", utils.ShQuote(pkg))
116
head = append(head, cmd)
118
script = append(head, tail...)
119
return strings.Join(script, "\n"), nil
122
func findMachineAgentTools(env environs.Environ, series, arch string) (*tools.Tools, error) {
123
possibleTools, err := envtools.FindInstanceTools(env, series, constraints.Value{})
127
arches := possibleTools.Arches()
128
possibleTools, err = possibleTools.Match(tools.Filter{Arch: arch})
130
return nil, fmt.Errorf("chosen architecture %v not present in %v", arch, arches)
132
return possibleTools[0], nil