1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
13
"launchpad.net/juju-core/environs"
14
"launchpad.net/juju-core/instance"
15
"launchpad.net/juju-core/juju"
16
"launchpad.net/juju-core/state"
17
"launchpad.net/juju-core/state/api"
18
"launchpad.net/juju-core/tools"
19
"launchpad.net/juju-core/utils"
20
"launchpad.net/juju-core/worker/provisioner"
23
const manualInstancePrefix = "manual:"
25
var logger = loggo.GetLogger("juju.environs.manual")
27
type ProvisionMachineArgs struct {
28
// Host is the SSH host: [user@]host
31
// DataDir is the root directory for juju data.
32
// If left blank, the default location "/var/lib/juju" will be used.
35
// State is the *state.State object to register the machine with.
38
// Tools to install on the machine. If nil, tools will be automatically
39
// chosen using environs/tools FindInstanceTools.
43
// ErrProvisioned is returned by ProvisionMachine if the target
44
// machine has an existing machine agent.
45
var ErrProvisioned = errors.New("machine is already provisioned")
47
// ProvisionMachine provisions a machine agent to an existing host, via
48
// an SSH connection to the specified host. The host may optionally be preceded
49
// with a login username, as in [user@]host.
51
// On successful completion, this function will return the state.Machine
52
// that was entered into state.
53
func ProvisionMachine(args ProvisionMachineArgs) (m *state.Machine, err error) {
55
if m != nil && err != nil {
62
var env environs.Environ
63
if conn, err := juju.NewConnFromState(args.State); err != nil {
69
sshHostWithoutUser := args.Host
70
if at := strings.Index(sshHostWithoutUser, "@"); at != -1 {
71
sshHostWithoutUser = sshHostWithoutUser[at+1:]
73
addrs, err := instance.HostAddresses(sshHostWithoutUser)
78
provisioned, err := checkProvisioned(args.Host)
80
err = fmt.Errorf("error checking if provisioned: %v", err)
84
return nil, ErrProvisioned
87
hc, series, err := detectSeriesAndHardwareCharacteristics(args.Host)
89
err = fmt.Errorf("error detecting hardware characteristics: %v", err)
93
// Generate a unique nonce for the machine.
94
uuid, err := utils.NewUUID()
99
// Inject a new machine into state.
101
// There will never be a corresponding "instance" that any provider
102
// knows about. This is fine, and works well with the provisioner
103
// task. The provisioner task will happily remove any and all dead
104
// machines from state, but will ignore the associated instance ID
105
// if it isn't one that the environment provider knows about.
106
instanceId := instance.Id(manualInstancePrefix + sshHostWithoutUser)
107
nonce := fmt.Sprintf("%s:%s", instanceId, uuid.String())
108
m, err = injectMachine(injectMachineArgs{
110
instanceId: instanceId,
119
stateInfo, apiInfo, err := setupAuthentication(env, m)
124
// Finally, provision the machine agent.
125
err = provisionMachineAgent(provisionMachineAgentArgs{
127
dataDir: args.DataDir,
131
stateInfo: stateInfo,
141
logger.Infof("Provisioned machine %v", m)
145
type injectMachineArgs struct {
147
instanceId instance.Id
148
addrs []instance.Address
150
hc instance.HardwareCharacteristics
154
// injectMachine injects a machine into state with provisioned status.
155
func injectMachine(args injectMachineArgs) (m *state.Machine, err error) {
157
if m != nil && err != nil {
162
m, err = args.st.InjectMachine(&state.AddMachineParams{
164
InstanceId: args.instanceId,
165
HardwareCharacteristics: args.hc,
167
Jobs: []state.MachineJob{state.JobHostUnits},
172
if err = m.SetAddresses(args.addrs); err != nil {
178
func setupAuthentication(env environs.Environ, m *state.Machine) (*state.Info, *api.Info, error) {
179
auth, err := provisioner.NewSimpleAuthenticator(env)
183
return auth.SetupAuthentication(m)