122
122
return nil, fmt.Errorf("environment is no longer alive")
125
var mdocs []*machineDoc
125
126
for _, template := range templates {
126
127
mdoc, addOps, err := st.addMachineOps(template)
131
mdocs = append(mdocs, mdoc)
130
132
ms = append(ms, newMachine(st, mdoc))
131
133
ops = append(ops, addOps...)
135
ssOps, err := st.maintainStateServersOps(mdocs, nil)
139
ops = append(ops, ssOps...)
133
140
ops = append(ops, env.assertAliveOp())
134
141
if err := st.runTransaction(ops); err != nil {
135
142
return nil, onAbort(err, fmt.Errorf("environment is no longer alive"))
389
func hasJob(jobs []MachineJob, job MachineJob) bool {
390
for _, j := range jobs {
398
var errStateServerNotAllowed = fmt.Errorf("state server jobs specified without calling EnsureAvailability")
400
// maintainStateServersOps returns a set of operations that will maintain
401
// the state server information when the given machine documents
402
// are added to the machines collection. If currentInfo is nil,
403
// there can be only one machine document and it must have
404
// id 0 (this is a special case to allow adding the bootstrap machine)
405
func (st *State) maintainStateServersOps(mdocs []*machineDoc, currentInfo *StateServerInfo) ([]txn.Op, error) {
406
var newIds, newVotingIds []string
407
for _, doc := range mdocs {
408
if !hasJob(doc.Jobs, JobManageEnviron) {
411
newIds = append(newIds, doc.Id)
413
newVotingIds = append(newVotingIds, doc.Id)
416
if len(newIds) == 0 {
419
if currentInfo == nil {
420
// Allow bootstrap machine only.
421
if len(mdocs) != 1 || mdocs[0].Id != "0" {
422
return nil, errStateServerNotAllowed
425
currentInfo, err = st.StateServerInfo()
427
return nil, fmt.Errorf("cannot get state server info: %v", err)
429
if len(currentInfo.MachineIds) > 0 || len(currentInfo.VotingMachineIds) > 0 {
430
return nil, fmt.Errorf("state servers already exist")
434
C: st.stateServers.Name,
435
Id: environGlobalKey,
438
{{"machineids", D{{"$size", len(currentInfo.MachineIds)}}}},
439
{{"votingmachineids", D{{"$size", len(currentInfo.VotingMachineIds)}}}},
443
{"$addToSet", D{{"machineids", D{{"$each", newIds}}}}},
444
{"$addToSet", D{{"votingmachineids", D{{"$each", newVotingIds}}}}},
450
// EnsureAvailability adds state server machines as necessary to make
451
// the number of live state servers equal to numStateServers. The given
452
// constraints and series will be attached to any new machines.
455
// If any current state servers are down, they will be
456
// removed from the current set of voting replica set
457
// peers (although the machines themselves will remain
458
// and they will still remain part of the replica set).
459
// Once a machine's voting status has been removed,
460
// the machine itself may be removed.
461
func (st *State) EnsureAvailability(numStateServers int, cons constraints.Value, series string) error {
462
if numStateServers%2 != 1 || numStateServers <= 0 {
463
return fmt.Errorf("number of state servers must be odd and greater than zero")
465
if numStateServers > replicaset.MaxPeers {
466
return fmt.Errorf("state server count is too large (allowed %d)", replicaset.MaxPeers)
468
info, err := st.StateServerInfo()
472
if len(info.VotingMachineIds) == numStateServers {
473
// TODO(rog) #1271504 2014-01-22
474
// Find machines which are down, set
475
// their NoVote flag and add new machines to
479
if len(info.VotingMachineIds) > numStateServers {
480
return fmt.Errorf("cannot reduce state server count")
482
mdocs := make([]*machineDoc, 0, numStateServers-len(info.MachineIds))
484
for i := len(info.MachineIds); i < numStateServers; i++ {
485
template := MachineTemplate{
493
mdoc, addOps, err := st.addMachineOps(template)
497
mdocs = append(mdocs, mdoc)
498
ops = append(ops, addOps...)
500
ssOps, err := st.maintainStateServersOps(mdocs, info)
502
return fmt.Errorf("cannot prepare machine add operations: %v", err)
504
ops = append(ops, ssOps...)
505
err = st.runTransaction(ops)
507
return fmt.Errorf("failed to create new state server machines: %v", err)