github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/apiserver/common/getstatus.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/leadership"
    14  	"github.com/juju/juju/lease"
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  // ErrIsNotLeader is an error for operations that require for a
    19  // unit to be the leader but it was not the case.
    20  var ErrIsNotLeader = errors.Errorf("this unit is not the leader")
    21  
    22  // StatusGetter implements a common Status method for use by
    23  // various facades.
    24  type StatusGetter struct {
    25  	st           state.EntityFinder
    26  	getcanAccess GetAuthFunc
    27  }
    28  
    29  // NewStatusGetter returns a new StatusGetter. The GetAuthFunc will be
    30  // used on each invocation of Status to determine current
    31  // permissions.
    32  func NewStatusGetter(st state.EntityFinder, getcanAccess GetAuthFunc) *StatusGetter {
    33  	return &StatusGetter{
    34  		st:           st,
    35  		getcanAccess: getcanAccess,
    36  	}
    37  }
    38  
    39  func (s *StatusGetter) getEntityStatus(tag names.Tag) params.StatusResult {
    40  	var result params.StatusResult
    41  	entity, err := s.st.FindEntity(tag)
    42  	if err != nil {
    43  		result.Error = ServerError(err)
    44  		return result
    45  	}
    46  	switch getter := entity.(type) {
    47  	case state.StatusGetter:
    48  		statusInfo, err := getter.Status()
    49  		result.Status = params.Status(statusInfo.Status)
    50  		result.Info = statusInfo.Message
    51  		result.Data = statusInfo.Data
    52  		result.Since = statusInfo.Since
    53  		result.Error = ServerError(err)
    54  	default:
    55  		result.Error = ServerError(NotSupportedError(tag, fmt.Sprintf("getting status, %T", getter)))
    56  	}
    57  	return result
    58  }
    59  
    60  // Status returns the status of each given entity.
    61  func (s *StatusGetter) Status(args params.Entities) (params.StatusResults, error) {
    62  	result := params.StatusResults{
    63  		Results: make([]params.StatusResult, len(args.Entities)),
    64  	}
    65  	canAccess, err := s.getcanAccess()
    66  	if err != nil {
    67  		return params.StatusResults{}, err
    68  	}
    69  	for i, entity := range args.Entities {
    70  		tag, err := names.ParseTag(entity.Tag)
    71  		if err != nil {
    72  			result.Results[i].Error = ServerError(err)
    73  			continue
    74  		}
    75  		if !canAccess(tag) {
    76  			result.Results[i].Error = ServerError(ErrPerm)
    77  			continue
    78  		}
    79  		result.Results[i] = s.getEntityStatus(tag)
    80  	}
    81  	return result, nil
    82  }
    83  
    84  // ServiceStatusGetter is a StatusGetter for combined service and unit statuses.
    85  type ServiceStatusGetter struct {
    86  	st           state.EntityFinder
    87  	getcanAccess GetAuthFunc
    88  }
    89  
    90  // NewServiceStatusGetter returns a ServiceStatusGetter.
    91  func NewServiceStatusGetter(st state.EntityFinder, getcanAccess GetAuthFunc) *ServiceStatusGetter {
    92  	return &ServiceStatusGetter{
    93  		st:           st,
    94  		getcanAccess: getcanAccess,
    95  	}
    96  }
    97  
    98  // StatusService interface represents an Service that can return Status for itself
    99  // and its Units.
   100  type StatusService interface {
   101  	state.Entity
   102  	Status() (state.StatusInfo, error)
   103  	ServiceAndUnitsStatus() (state.StatusInfo, map[string]state.StatusInfo, error)
   104  	SetStatus(state.Status, string, map[string]interface{}) error
   105  }
   106  
   107  type serviceGetter func(state.EntityFinder, string) (StatusService, error)
   108  
   109  type isLeaderFunc func(state.EntityFinder, string) (bool, error)
   110  
   111  func getUnit(st state.EntityFinder, unitTag string) (*state.Unit, error) {
   112  	tag, err := names.ParseUnitTag(unitTag)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	entity, err := st.FindEntity(tag)
   117  	if err != nil {
   118  
   119  		return nil, err
   120  	}
   121  	unit, ok := entity.(*state.Unit)
   122  	if !ok {
   123  		return nil, err
   124  	}
   125  	return unit, nil
   126  }
   127  
   128  func serviceFromUnitTag(st state.EntityFinder, unitTag string) (StatusService, error) {
   129  	unit, err := getUnit(st, unitTag)
   130  	if err != nil {
   131  		return nil, errors.Annotatef(err, "cannot obtain unit %q to obtain service", unitTag)
   132  	}
   133  	var service StatusService
   134  	service, err = unit.Service()
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	return service, nil
   139  }
   140  
   141  // isLeader will return true if the unitTag passed corresponds to the leader.
   142  // TODO(perrito666): this must go in 1.25, it is not a fault proof approach
   143  // it cannot guarantee that the unit will be leader during the duration of
   144  // wathever operation we are doing nor has a sane approach to obtaining a
   145  // lease manager.
   146  func isLeader(st state.EntityFinder, unitTag string) (bool, error) {
   147  
   148  	unit, err := getUnit(st, unitTag)
   149  	if err != nil {
   150  		return false, errors.Annotatef(err, "cannot obtain unit %q to check leadership", unitTag)
   151  	}
   152  	service, err := unit.Service()
   153  	if err != nil {
   154  		return false, err
   155  	}
   156  
   157  	leaseManager := lease.Manager()
   158  	leadershipManager := leadership.NewLeadershipManager(leaseManager)
   159  	return leadershipManager.Leader(service.Name(), unit.Name())
   160  }
   161  
   162  // Status returns the status of the Service for each given Unit tag.
   163  func (s *ServiceStatusGetter) Status(args params.Entities) (params.ServiceStatusResults, error) {
   164  	return serviceStatus(s, args, serviceFromUnitTag, isLeader)
   165  }
   166  
   167  func serviceStatus(s *ServiceStatusGetter, args params.Entities, getService serviceGetter, isLeaderCheck isLeaderFunc) (params.ServiceStatusResults, error) {
   168  	results := params.ServiceStatusResults{
   169  		Results: make([]params.ServiceStatusResult, len(args.Entities)),
   170  	}
   171  	canAccess, err := s.getcanAccess()
   172  	if err != nil {
   173  		return params.ServiceStatusResults{}, err
   174  	}
   175  
   176  	for i, serviceUnit := range args.Entities {
   177  		leader, err := isLeaderCheck(s.st, serviceUnit.Tag)
   178  		if err != nil {
   179  			results.Results[i].Error = ServerError(err)
   180  			continue
   181  		}
   182  		if !leader {
   183  			results.Results[i].Error = ServerError(ErrIsNotLeader)
   184  			continue
   185  		}
   186  		var service StatusService
   187  		service, err = getService(s.st, serviceUnit.Tag)
   188  		if err != nil {
   189  			results.Results[i].Error = ServerError(err)
   190  			continue
   191  		}
   192  		if !canAccess(service.Tag()) {
   193  			results.Results[i].Error = ServerError(ErrPerm)
   194  			continue
   195  		}
   196  
   197  		serviceStatus, unitStatuses, err := service.ServiceAndUnitsStatus()
   198  		if err != nil {
   199  			results.Results[i].Service.Error = ServerError(err)
   200  			results.Results[i].Error = ServerError(err)
   201  			continue
   202  		}
   203  		results.Results[i].Service.Status = params.Status(serviceStatus.Status)
   204  		results.Results[i].Service.Info = serviceStatus.Message
   205  		results.Results[i].Service.Data = serviceStatus.Data
   206  		results.Results[i].Service.Since = serviceStatus.Since
   207  
   208  		results.Results[i].Units = make(map[string]params.StatusResult, len(unitStatuses))
   209  		for uTag, r := range unitStatuses {
   210  			ur := params.StatusResult{
   211  				Status: params.Status(r.Status),
   212  				Info:   r.Message,
   213  				Data:   r.Data,
   214  				Since:  r.Since,
   215  			}
   216  			results.Results[i].Units[uTag] = ur
   217  		}
   218  	}
   219  	return results, nil
   220  }