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