// EnsureAvailabilitySingle applies a single StateServersSpec specification to the current environment. // Exported so it can be called by the legacy client API in the client package. func EnsureAvailabilitySingle(st *state.State, spec params.StateServersSpec) (params.StateServersChanges, error) { if !st.IsStateServer() { return params.StateServersChanges{}, errors.New("unsupported with hosted environments") } // Check if changes are allowed and the command may proceed. blockChecker := common.NewBlockChecker(st) if err := blockChecker.ChangeAllowed(); err != nil { return params.StateServersChanges{}, errors.Trace(err) } // Validate the environment tag if present. if spec.EnvironTag != "" { tag, err := names.ParseEnvironTag(spec.EnvironTag) if err != nil { return params.StateServersChanges{}, errors.Errorf("invalid environment tag: %v", err) } if _, err := st.FindEntity(tag); err != nil { return params.StateServersChanges{}, err } } series := spec.Series if series == "" { ssi, err := st.StateServerInfo() if err != nil { return params.StateServersChanges{}, err } // We should always have at least one voting machine // If we *really* wanted we could just pick whatever series is // in the majority, but really, if we always copy the value of // the first one, then they'll stay in sync. if len(ssi.VotingMachineIds) == 0 { // Better than a panic()? return params.StateServersChanges{}, fmt.Errorf("internal error, failed to find any voting machines") } templateMachine, err := st.Machine(ssi.VotingMachineIds[0]) if err != nil { return params.StateServersChanges{}, err } series = templateMachine.Series() } changes, err := st.EnsureAvailability(spec.NumStateServers, spec.Constraints, series, spec.Placement) if err != nil { return params.StateServersChanges{}, err } return stateServersChanges(changes), nil }