-
Notifications
You must be signed in to change notification settings - Fork 1
/
bootstrap.go
executable file
·164 lines (147 loc) · 5.73 KB
/
bootstrap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package agent
import (
"fmt"
"launchpad.net/juju-core/constraints"
"launchpad.net/juju-core/environs/config"
"launchpad.net/juju-core/instance"
"launchpad.net/juju-core/names"
"launchpad.net/juju-core/state"
"launchpad.net/juju-core/state/api/params"
"launchpad.net/juju-core/version"
)
// InitializeState should be called on the bootstrap machine's agent
// configuration. It uses that information to dial the state server and
// initialize it. It also generates a new password for the bootstrap
// machine and calls Write to save the the configuration.
//
// The envCfg values will be stored in the state's EnvironConfig; the
// machineCfg values will be used to configure the bootstrap Machine,
// and its constraints will be also be used for the environment-level
// constraints. The connection to the state server will respect the
// given timeout parameter.
//
// InitializeState returns the newly initialized state and bootstrap
// machine. If it fails, the state may well be irredeemably compromised.
type StateInitializer interface {
InitializeState(envCfg *config.Config, machineCfg BootstrapMachineConfig, timeout state.DialOpts, policy state.Policy) (*state.State, *state.Machine, error)
}
// BootstrapMachineConfig holds configuration information
// to attach to the bootstrap machine.
type BootstrapMachineConfig struct {
// Constraints holds the bootstrap machine's constraints.
// This value is also used for the environment-level constraints.
Constraints constraints.Value
// Jobs holds the jobs that the machine agent will run.
Jobs []params.MachineJob
// InstanceId holds the instance id of the bootstrap machine.
InstanceId instance.Id
// Characteristics holds hardware information on the
// bootstrap machine.
Characteristics instance.HardwareCharacteristics
}
const bootstrapMachineId = "0"
func (c *configInternal) InitializeState(envCfg *config.Config, machineCfg BootstrapMachineConfig, timeout state.DialOpts, policy state.Policy) (*state.State, *state.Machine, error) {
if c.Tag() != names.MachineTag(bootstrapMachineId) {
return nil, nil, fmt.Errorf("InitializeState not called with bootstrap machine's configuration")
}
info := state.Info{
Addrs: c.stateDetails.addresses,
CACert: c.caCert,
}
logger.Debugf("initializing address %v", info.Addrs)
st, err := state.Initialize(&info, envCfg, timeout, policy)
if err != nil {
return nil, nil, fmt.Errorf("failed to initialize state: %v", err)
}
logger.Debugf("connected to initial state")
m, err := c.initUsersAndBootstrapMachine(st, machineCfg)
if err != nil {
st.Close()
return nil, nil, err
}
return st, m, nil
}
func (c *configInternal) initUsersAndBootstrapMachine(st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) {
if err := initBootstrapUser(st, c.oldPassword); err != nil {
return nil, fmt.Errorf("cannot initialize bootstrap user: %v", err)
}
if err := st.SetEnvironConstraints(cfg.Constraints); err != nil {
return nil, fmt.Errorf("cannot set initial environ constraints: %v", err)
}
m, err := c.initBootstrapMachine(st, cfg)
if err != nil {
return nil, fmt.Errorf("cannot initialize bootstrap machine: %v", err)
}
return m, nil
}
// initBootstrapUser creates the initial admin user for the database, and sets
// the initial password.
func initBootstrapUser(st *state.State, passwordHash string) error {
logger.Debugf("adding admin user")
// Set up initial authentication.
u, err := st.AddUser("admin", "")
if err != nil {
return err
}
// Note that at bootstrap time, the password is set to
// the hash of its actual value. The first time a client
// connects to mongo, it changes the mongo password
// to the original password.
logger.Debugf("setting password hash for admin user")
// TODO(jam): http://pad.lv/1248839
// We could teach bootstrap how to generate a custom salt and apply
// that to the hash that was generated. At which point we'd need to set
// it here. For now, we pass "" so that on first login we will create a
// new salt, but the fixed-salt password is still available from
// cloud-init.
if err := u.SetPasswordHash(passwordHash, ""); err != nil {
return err
}
if err := st.SetAdminMongoPassword(passwordHash); err != nil {
return err
}
return nil
}
// initBootstrapMachine initializes the initial bootstrap machine in state.
func (c *configInternal) initBootstrapMachine(st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) {
logger.Infof("initialising bootstrap machine with config: %+v", cfg)
jobs := make([]state.MachineJob, len(cfg.Jobs))
for i, job := range cfg.Jobs {
machineJob, err := state.MachineJobFromParams(job)
if err != nil {
return nil, fmt.Errorf("invalid bootstrap machine job %q: %v", job, err)
}
jobs[i] = machineJob
}
m, err := st.AddOneMachine(state.MachineTemplate{
Series: version.Current.Series,
Nonce: state.BootstrapNonce,
Constraints: cfg.Constraints,
InstanceId: cfg.InstanceId,
HardwareCharacteristics: cfg.Characteristics,
Jobs: jobs,
})
if err != nil {
return nil, fmt.Errorf("cannot create bootstrap machine in state: %v", err)
}
if m.Id() != bootstrapMachineId {
return nil, fmt.Errorf("bootstrap machine expected id 0, got %q", m.Id())
}
// Read the machine agent's password and change it to
// a new password (other agents will change their password
// via the API connection).
logger.Debugf("create new random password for machine %v", m.Id())
newPassword, err := c.writeNewPassword()
if err != nil {
return nil, err
}
if err := m.SetMongoPassword(newPassword); err != nil {
return nil, err
}
if err := m.SetPassword(newPassword); err != nil {
return nil, err
}
return m, nil
}