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