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  }