github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/agent/agentbootstrap/bootstrap.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agentbootstrap 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names" 10 "github.com/juju/utils" 11 "github.com/juju/utils/series" 12 13 "github.com/juju/juju/agent" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/constraints" 16 "github.com/juju/juju/controller/modelmanager" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/instance" 19 "github.com/juju/juju/mongo" 20 "github.com/juju/juju/network" 21 "github.com/juju/juju/state" 22 "github.com/juju/juju/state/multiwatcher" 23 ) 24 25 var logger = loggo.GetLogger("juju.agent.agentbootstrap") 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 // BootstrapConstraints holds the bootstrap machine's constraints. 34 BootstrapConstraints constraints.Value 35 36 // ModelConstraints holds the model-level constraints. 37 ModelConstraints constraints.Value 38 39 // Jobs holds the jobs that the machine agent will run. 40 Jobs []multiwatcher.MachineJob 41 42 // InstanceId holds the instance id of the bootstrap machine. 43 InstanceId instance.Id 44 45 // Characteristics holds hardware information on the 46 // bootstrap machine. 47 Characteristics instance.HardwareCharacteristics 48 49 // SharedSecret is the Mongo replica set shared secret (keyfile). 50 SharedSecret string 51 } 52 53 // InitializeState should be called on the bootstrap machine's agent 54 // configuration. It uses that information to create the controller, dial the 55 // controller, 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 cfg values will be stored in the state's ModelConfig; the 59 // machineCfg values will be used to configure the bootstrap Machine, 60 // and its constraints will be also be used for the model-level 61 // constraints. The connection to the controller 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( 67 adminUser names.UserTag, 68 c agent.ConfigSetter, 69 cfg *config.Config, 70 hostedModelConfigAttrs map[string]interface{}, 71 machineCfg BootstrapMachineConfig, 72 dialOpts mongo.DialOpts, 73 policy state.Policy, 74 ) (_ *state.State, _ *state.Machine, resultErr error) { 75 if c.Tag() != names.NewMachineTag(agent.BootstrapMachineId) { 76 return nil, nil, errors.Errorf("InitializeState not called with bootstrap machine's configuration") 77 } 78 servingInfo, ok := c.StateServingInfo() 79 if !ok { 80 return nil, nil, errors.Errorf("state serving information not available") 81 } 82 // N.B. no users are set up when we're initializing the state, 83 // so don't use any tag or password when opening it. 84 info, ok := c.MongoInfo() 85 if !ok { 86 return nil, nil, errors.Errorf("stateinfo not available") 87 } 88 info.Tag = nil 89 info.Password = c.OldPassword() 90 91 if err := initMongoAdminUser(info.Info, dialOpts, info.Password); err != nil { 92 return nil, nil, errors.Annotate(err, "failed to initialize mongo admin user") 93 } 94 95 logger.Debugf("initializing address %v", info.Addrs) 96 st, err := state.Initialize(adminUser, info, cfg, dialOpts, policy) 97 if err != nil { 98 return nil, nil, errors.Errorf("failed to initialize state: %v", err) 99 } 100 logger.Debugf("connected to initial state") 101 defer func() { 102 if resultErr != nil { 103 st.Close() 104 } 105 }() 106 servingInfo.SharedSecret = machineCfg.SharedSecret 107 c.SetStateServingInfo(servingInfo) 108 109 // Filter out any LXC bridge addresses from the machine addresses. 110 machineCfg.Addresses = network.FilterLXCAddresses(machineCfg.Addresses) 111 112 if err = initAPIHostPorts(c, st, machineCfg.Addresses, servingInfo.APIPort); err != nil { 113 return nil, nil, err 114 } 115 ssi := paramsStateServingInfoToStateStateServingInfo(servingInfo) 116 if err := st.SetStateServingInfo(ssi); err != nil { 117 return nil, nil, errors.Errorf("cannot set state serving info: %v", err) 118 } 119 m, err := initConstraintsAndBootstrapMachine(c, st, machineCfg) 120 if err != nil { 121 return nil, nil, err 122 } 123 124 // Create the initial hosted model, with the model config passed to 125 // bootstrap, which contains the UUID, name for the hosted model, 126 // and any user supplied config. 127 attrs := make(map[string]interface{}) 128 for k, v := range hostedModelConfigAttrs { 129 attrs[k] = v 130 } 131 hostedModelConfig, err := modelmanager.ModelConfigCreator{}.NewModelConfig(modelmanager.IsAdmin, cfg, attrs) 132 if err != nil { 133 return nil, nil, errors.Annotate(err, "creating hosted model config") 134 } 135 _, hostedModelState, err := st.NewModel(state.ModelArgs{ 136 Config: hostedModelConfig, 137 Owner: adminUser, 138 }) 139 if err != nil { 140 return nil, nil, errors.Annotate(err, "creating hosted model") 141 } 142 if err := hostedModelState.SetModelConstraints(machineCfg.ModelConstraints); err != nil { 143 return nil, nil, errors.Annotate(err, "cannot set initial hosted model constraints") 144 } 145 hostedModelState.Close() 146 147 return st, m, nil 148 } 149 150 func paramsStateServingInfoToStateStateServingInfo(i params.StateServingInfo) state.StateServingInfo { 151 return state.StateServingInfo{ 152 APIPort: i.APIPort, 153 StatePort: i.StatePort, 154 Cert: i.Cert, 155 PrivateKey: i.PrivateKey, 156 CAPrivateKey: i.CAPrivateKey, 157 SharedSecret: i.SharedSecret, 158 SystemIdentity: i.SystemIdentity, 159 } 160 } 161 162 func initConstraintsAndBootstrapMachine(c agent.ConfigSetter, st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { 163 if err := st.SetModelConstraints(cfg.ModelConstraints); err != nil { 164 return nil, errors.Annotate(err, "cannot set initial model constraints") 165 } 166 m, err := initBootstrapMachine(c, st, cfg) 167 if err != nil { 168 return nil, errors.Annotate(err, "cannot initialize bootstrap machine") 169 } 170 return m, nil 171 } 172 173 // initMongoAdminUser adds the admin user with the specified 174 // password to the admin database in Mongo. 175 func initMongoAdminUser(info mongo.Info, dialOpts mongo.DialOpts, password string) error { 176 session, err := mongo.DialWithInfo(info, dialOpts) 177 if err != nil { 178 return err 179 } 180 defer session.Close() 181 return mongo.SetAdminMongoPassword(session, mongo.AdminUser, password) 182 } 183 184 // initBootstrapMachine initializes the initial bootstrap machine in state. 185 func initBootstrapMachine(c agent.ConfigSetter, st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { 186 logger.Infof("initialising bootstrap machine with config: %+v", cfg) 187 188 jobs := make([]state.MachineJob, len(cfg.Jobs)) 189 for i, job := range cfg.Jobs { 190 machineJob, err := machineJobFromParams(job) 191 if err != nil { 192 return nil, errors.Errorf("invalid bootstrap machine job %q: %v", job, err) 193 } 194 jobs[i] = machineJob 195 } 196 m, err := st.AddOneMachine(state.MachineTemplate{ 197 Addresses: cfg.Addresses, 198 Series: series.HostSeries(), 199 Nonce: agent.BootstrapNonce, 200 Constraints: cfg.BootstrapConstraints, 201 InstanceId: cfg.InstanceId, 202 HardwareCharacteristics: cfg.Characteristics, 203 Jobs: jobs, 204 }) 205 if err != nil { 206 return nil, errors.Errorf("cannot create bootstrap machine in state: %v", err) 207 } 208 if m.Id() != agent.BootstrapMachineId { 209 return nil, errors.Errorf("bootstrap machine expected id 0, got %q", m.Id()) 210 } 211 // Read the machine agent's password and change it to 212 // a new password (other agents will change their password 213 // via the API connection). 214 logger.Debugf("create new random password for machine %v", m.Id()) 215 216 newPassword, err := utils.RandomPassword() 217 if err != nil { 218 return nil, err 219 } 220 if err := m.SetPassword(newPassword); err != nil { 221 return nil, err 222 } 223 if err := m.SetMongoPassword(newPassword); err != nil { 224 return nil, err 225 } 226 c.SetPassword(newPassword) 227 return m, nil 228 } 229 230 // initAPIHostPorts sets the initial API host/port addresses in state. 231 func initAPIHostPorts(c agent.ConfigSetter, st *state.State, addrs []network.Address, apiPort int) error { 232 var hostPorts []network.HostPort 233 // First try to select the correct address using the default space where all 234 // API servers should be accessible on. 235 spaceAddr, ok := network.SelectAddressBySpaces(addrs) 236 if ok { 237 logger.Debugf("selected %q as API address", spaceAddr.Value) 238 hostPorts = network.AddressesWithPort([]network.Address{spaceAddr}, apiPort) 239 } else { 240 // Fallback to using all instead. 241 hostPorts = network.AddressesWithPort(addrs, apiPort) 242 } 243 244 return st.SetAPIHostPorts([][]network.HostPort{hostPorts}) 245 } 246 247 // machineJobFromParams returns the job corresponding to params.MachineJob. 248 // TODO(dfc) this function should live in apiserver/params, move there once 249 // state does not depend on apiserver/params 250 func machineJobFromParams(job multiwatcher.MachineJob) (state.MachineJob, error) { 251 switch job { 252 case multiwatcher.JobHostUnits: 253 return state.JobHostUnits, nil 254 case multiwatcher.JobManageModel: 255 return state.JobManageModel, nil 256 case multiwatcher.JobManageNetworking: 257 return state.JobManageNetworking, nil 258 default: 259 return -1, errors.Errorf("invalid machine job %q", job) 260 } 261 }