github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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 "github.com/juju/errors" 8 "github.com/juju/names" 9 "github.com/juju/utils" 10 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/constraints" 13 "github.com/juju/juju/environs/config" 14 "github.com/juju/juju/instance" 15 "github.com/juju/juju/mongo" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/state/multiwatcher" 19 "github.com/juju/juju/version" 20 ) 21 22 const ( 23 // BootstrapNonce is used as a nonce for the state server machine. 24 BootstrapNonce = "user-admin:bootstrap" 25 ) 26 27 // BootstrapMachineConfig holds configuration information 28 // to attach to the bootstrap machine. 29 type BootstrapMachineConfig struct { 30 // Addresses holds the bootstrap machine's addresses. 31 Addresses []network.Address 32 33 // Constraints holds the bootstrap machine's constraints. 34 // This value is also used for the environment-level constraints. 35 Constraints constraints.Value 36 37 // Jobs holds the jobs that the machine agent will run. 38 Jobs []multiwatcher.MachineJob 39 40 // InstanceId holds the instance id of the bootstrap machine. 41 InstanceId instance.Id 42 43 // Characteristics holds hardware information on the 44 // bootstrap machine. 45 Characteristics instance.HardwareCharacteristics 46 47 // SharedSecret is the Mongo replica set shared secret (keyfile). 48 SharedSecret string 49 } 50 51 const BootstrapMachineId = "0" 52 53 // InitializeState should be called on the bootstrap machine's agent 54 // configuration. It uses that information to create the state server, dial the 55 // state server, and initialize it. It also generates a new password for the 56 // bootstrap machine and calls Write to save the the configuration. 57 // 58 // The envCfg values will be stored in the state's EnvironConfig; the 59 // machineCfg values will be used to configure the bootstrap Machine, 60 // and its constraints will be also be used for the environment-level 61 // constraints. The connection to the state server will respect the 62 // given timeout parameter. 63 // 64 // InitializeState returns the newly initialized state and bootstrap 65 // machine. If it fails, the state may well be irredeemably compromised. 66 func InitializeState(adminUser names.UserTag, c ConfigSetter, envCfg *config.Config, machineCfg BootstrapMachineConfig, dialOpts mongo.DialOpts, policy state.Policy) (_ *state.State, _ *state.Machine, resultErr error) { 67 if c.Tag() != names.NewMachineTag(BootstrapMachineId) { 68 return nil, nil, errors.Errorf("InitializeState not called with bootstrap machine's configuration") 69 } 70 servingInfo, ok := c.StateServingInfo() 71 if !ok { 72 return nil, nil, errors.Errorf("state serving information not available") 73 } 74 // N.B. no users are set up when we're initializing the state, 75 // so don't use any tag or password when opening it. 76 info, ok := c.MongoInfo() 77 if !ok { 78 return nil, nil, errors.Errorf("stateinfo not available") 79 } 80 info.Tag = nil 81 info.Password = c.OldPassword() 82 83 if err := initMongoAdminUser(info.Info, dialOpts, info.Password); err != nil { 84 return nil, nil, errors.Annotate(err, "failed to initialize mongo admin user") 85 } 86 87 logger.Debugf("initializing address %v", info.Addrs) 88 st, err := state.Initialize(adminUser, info, envCfg, dialOpts, policy) 89 if err != nil { 90 return nil, nil, errors.Errorf("failed to initialize state: %v", err) 91 } 92 logger.Debugf("connected to initial state") 93 defer func() { 94 if resultErr != nil { 95 st.Close() 96 } 97 }() 98 servingInfo.SharedSecret = machineCfg.SharedSecret 99 c.SetStateServingInfo(servingInfo) 100 if err = initAPIHostPorts(c, st, machineCfg.Addresses, servingInfo.APIPort); err != nil { 101 return nil, nil, err 102 } 103 ssi := paramsStateServingInfoToStateStateServingInfo(servingInfo) 104 if err := st.SetStateServingInfo(ssi); err != nil { 105 return nil, nil, errors.Errorf("cannot set state serving info: %v", err) 106 } 107 m, err := initConstraintsAndBootstrapMachine(c, st, machineCfg) 108 if err != nil { 109 return nil, nil, err 110 } 111 return st, m, nil 112 } 113 114 func paramsStateServingInfoToStateStateServingInfo(i params.StateServingInfo) state.StateServingInfo { 115 return state.StateServingInfo{ 116 APIPort: i.APIPort, 117 StatePort: i.StatePort, 118 Cert: i.Cert, 119 PrivateKey: i.PrivateKey, 120 CAPrivateKey: i.CAPrivateKey, 121 SharedSecret: i.SharedSecret, 122 SystemIdentity: i.SystemIdentity, 123 } 124 } 125 126 func initConstraintsAndBootstrapMachine(c ConfigSetter, st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { 127 if err := st.SetEnvironConstraints(cfg.Constraints); err != nil { 128 return nil, errors.Errorf("cannot set initial environ constraints: %v", err) 129 } 130 m, err := initBootstrapMachine(c, st, cfg) 131 if err != nil { 132 return nil, errors.Errorf("cannot initialize bootstrap machine: %v", err) 133 } 134 return m, nil 135 } 136 137 // initMongoAdminUser adds the admin user with the specified 138 // password to the admin database in Mongo. 139 func initMongoAdminUser(info mongo.Info, dialOpts mongo.DialOpts, password string) error { 140 session, err := mongo.DialWithInfo(info, dialOpts) 141 if err != nil { 142 return err 143 } 144 defer session.Close() 145 return mongo.SetAdminMongoPassword(session, mongo.AdminUser, password) 146 } 147 148 // initBootstrapMachine initializes the initial bootstrap machine in state. 149 func initBootstrapMachine(c ConfigSetter, st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { 150 logger.Infof("initialising bootstrap machine with config: %+v", cfg) 151 152 jobs := make([]state.MachineJob, len(cfg.Jobs)) 153 for i, job := range cfg.Jobs { 154 machineJob, err := machineJobFromParams(job) 155 if err != nil { 156 return nil, errors.Errorf("invalid bootstrap machine job %q: %v", job, err) 157 } 158 jobs[i] = machineJob 159 } 160 m, err := st.AddOneMachine(state.MachineTemplate{ 161 Addresses: cfg.Addresses, 162 Series: version.Current.Series, 163 Nonce: BootstrapNonce, 164 Constraints: cfg.Constraints, 165 InstanceId: cfg.InstanceId, 166 HardwareCharacteristics: cfg.Characteristics, 167 Jobs: jobs, 168 }) 169 if err != nil { 170 return nil, errors.Errorf("cannot create bootstrap machine in state: %v", err) 171 } 172 if m.Id() != BootstrapMachineId { 173 return nil, errors.Errorf("bootstrap machine expected id 0, got %q", m.Id()) 174 } 175 // Read the machine agent's password and change it to 176 // a new password (other agents will change their password 177 // via the API connection). 178 logger.Debugf("create new random password for machine %v", m.Id()) 179 180 newPassword, err := utils.RandomPassword() 181 if err != nil { 182 return nil, err 183 } 184 if err := m.SetPassword(newPassword); err != nil { 185 return nil, err 186 } 187 if err := m.SetMongoPassword(newPassword); err != nil { 188 return nil, err 189 } 190 c.SetPassword(newPassword) 191 return m, nil 192 } 193 194 // initAPIHostPorts sets the initial API host/port addresses in state. 195 func initAPIHostPorts(c ConfigSetter, st *state.State, addrs []network.Address, apiPort int) error { 196 hostPorts := network.AddressesWithPort(addrs, apiPort) 197 return st.SetAPIHostPorts([][]network.HostPort{hostPorts}) 198 } 199 200 // machineJobFromParams returns the job corresponding to params.MachineJob. 201 // TODO(dfc) this function should live in apiserver/params, move there once 202 // state does not depend on apiserver/params 203 func machineJobFromParams(job multiwatcher.MachineJob) (state.MachineJob, error) { 204 switch job { 205 case multiwatcher.JobHostUnits: 206 return state.JobHostUnits, nil 207 case multiwatcher.JobManageEnviron: 208 return state.JobManageEnviron, nil 209 case multiwatcher.JobManageNetworking: 210 return state.JobManageNetworking, nil 211 case multiwatcher.JobManageStateDeprecated: 212 // Deprecated in 1.18. 213 return state.JobManageStateDeprecated, nil 214 default: 215 return -1, errors.Errorf("invalid machine job %q", job) 216 } 217 }