github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/highavailability/highavailability.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package highavailability 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/state" 15 ) 16 17 func init() { 18 common.RegisterStandardFacade("HighAvailability", 1, NewHighAvailabilityAPI) 19 } 20 21 // HighAvailability defines the methods on the highavailability API end point. 22 type HighAvailability interface { 23 EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) 24 } 25 26 // HighAvailabilityAPI implements the HighAvailability interface and is the concrete 27 // implementation of the api end point. 28 type HighAvailabilityAPI struct { 29 state *state.State 30 resources *common.Resources 31 authorizer common.Authorizer 32 } 33 34 var _ HighAvailability = (*HighAvailabilityAPI)(nil) 35 36 // NewHighAvailabilityAPI creates a new server-side highavailability API end point. 37 func NewHighAvailabilityAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*HighAvailabilityAPI, error) { 38 // Only clients and environment managers can access the high availability service. 39 if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() { 40 return nil, common.ErrPerm 41 } 42 return &HighAvailabilityAPI{ 43 state: st, 44 resources: resources, 45 authorizer: authorizer, 46 }, nil 47 } 48 49 func (api *HighAvailabilityAPI) EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) { 50 results := params.StateServersChangeResults{Results: make([]params.StateServersChangeResult, len(args.Specs))} 51 for i, stateServersSpec := range args.Specs { 52 result, err := EnsureAvailabilitySingle(api.state, stateServersSpec) 53 results.Results[i].Result = result 54 results.Results[i].Error = common.ServerError(err) 55 } 56 return results, nil 57 } 58 59 // Convert machine ids to tags. 60 func machineIdsToTags(ids ...string) []string { 61 var result []string 62 for _, id := range ids { 63 result = append(result, names.NewMachineTag(id).String()) 64 } 65 return result 66 } 67 68 // Generate a StateServersChanges structure. 69 func stateServersChanges(change state.StateServersChanges) params.StateServersChanges { 70 return params.StateServersChanges{ 71 Added: machineIdsToTags(change.Added...), 72 Maintained: machineIdsToTags(change.Maintained...), 73 Removed: machineIdsToTags(change.Removed...), 74 Promoted: machineIdsToTags(change.Promoted...), 75 Demoted: machineIdsToTags(change.Demoted...), 76 Converted: machineIdsToTags(change.Converted...), 77 } 78 } 79 80 // EnsureAvailabilitySingle applies a single StateServersSpec specification to the current environment. 81 // Exported so it can be called by the legacy client API in the client package. 82 func EnsureAvailabilitySingle(st *state.State, spec params.StateServersSpec) (params.StateServersChanges, error) { 83 if !st.IsStateServer() { 84 return params.StateServersChanges{}, errors.New("unsupported with hosted environments") 85 } 86 // Check if changes are allowed and the command may proceed. 87 blockChecker := common.NewBlockChecker(st) 88 if err := blockChecker.ChangeAllowed(); err != nil { 89 return params.StateServersChanges{}, errors.Trace(err) 90 } 91 // Validate the environment tag if present. 92 if spec.EnvironTag != "" { 93 tag, err := names.ParseEnvironTag(spec.EnvironTag) 94 if err != nil { 95 return params.StateServersChanges{}, errors.Errorf("invalid environment tag: %v", err) 96 } 97 if _, err := st.FindEntity(tag); err != nil { 98 return params.StateServersChanges{}, err 99 } 100 } 101 102 series := spec.Series 103 if series == "" { 104 ssi, err := st.StateServerInfo() 105 if err != nil { 106 return params.StateServersChanges{}, err 107 } 108 109 // We should always have at least one voting machine 110 // If we *really* wanted we could just pick whatever series is 111 // in the majority, but really, if we always copy the value of 112 // the first one, then they'll stay in sync. 113 if len(ssi.VotingMachineIds) == 0 { 114 // Better than a panic()? 115 return params.StateServersChanges{}, fmt.Errorf("internal error, failed to find any voting machines") 116 } 117 templateMachine, err := st.Machine(ssi.VotingMachineIds[0]) 118 if err != nil { 119 return params.StateServersChanges{}, err 120 } 121 series = templateMachine.Series() 122 } 123 changes, err := st.EnsureAvailability(spec.NumStateServers, spec.Constraints, series, spec.Placement) 124 if err != nil { 125 return params.StateServersChanges{}, err 126 } 127 return stateServersChanges(changes), nil 128 }