github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/core/status"
    13  	"github.com/juju/juju/state"
    14  )
    15  
    16  // StatusGetter implements a common Status method for use by
    17  // various facades.
    18  type StatusGetter struct {
    19  	st           state.EntityFinder
    20  	getCanAccess GetAuthFunc
    21  }
    22  
    23  // NewStatusGetter returns a new StatusGetter. The GetAuthFunc will be
    24  // used on each invocation of Status to determine current
    25  // permissions.
    26  func NewStatusGetter(st state.EntityFinder, getCanAccess GetAuthFunc) *StatusGetter {
    27  	return &StatusGetter{
    28  		st:           st,
    29  		getCanAccess: getCanAccess,
    30  	}
    31  }
    32  
    33  func (s *StatusGetter) getEntityStatus(tag names.Tag) params.StatusResult {
    34  	var result params.StatusResult
    35  	entity, err := s.st.FindEntity(tag)
    36  	if err != nil {
    37  		result.Error = ServerError(err)
    38  		return result
    39  	}
    40  	switch getter := entity.(type) {
    41  	case status.StatusGetter:
    42  		statusInfo, err := getter.Status()
    43  		result.Status = statusInfo.Status.String()
    44  		result.Info = statusInfo.Message
    45  		result.Data = statusInfo.Data
    46  		result.Since = statusInfo.Since
    47  		result.Error = ServerError(err)
    48  	default:
    49  		result.Error = ServerError(NotSupportedError(tag, fmt.Sprintf("getting status, %T", getter)))
    50  	}
    51  	return result
    52  }
    53  
    54  // Status returns the status of each given entity.
    55  func (s *StatusGetter) Status(args params.Entities) (params.StatusResults, error) {
    56  	result := params.StatusResults{
    57  		Results: make([]params.StatusResult, len(args.Entities)),
    58  	}
    59  	canAccess, err := s.getCanAccess()
    60  	if err != nil {
    61  		return params.StatusResults{}, err
    62  	}
    63  	for i, entity := range args.Entities {
    64  		tag, err := names.ParseTag(entity.Tag)
    65  		if err != nil {
    66  			result.Results[i].Error = ServerError(err)
    67  			continue
    68  		}
    69  		if !canAccess(tag) {
    70  			result.Results[i].Error = ServerError(ErrPerm)
    71  			continue
    72  		}
    73  		result.Results[i] = s.getEntityStatus(tag)
    74  	}
    75  	return result, nil
    76  }
    77  
    78  // ApplicationStatusGetter is a StatusGetter for combined application and unit statuses.
    79  // TODO(fwereade) this is completely evil and should never have been created.
    80  // We have a perfectly adequate StatusGetter already, that accepts bulk args;
    81  // all this does is break the user model, break the api model, and lie about
    82  // unit statuses).
    83  type ApplicationStatusGetter struct {
    84  	st           *state.State
    85  	getCanAccess GetAuthFunc
    86  }
    87  
    88  // NewApplicationStatusGetter returns a ApplicationStatusGetter.
    89  func NewApplicationStatusGetter(st *state.State, getCanAccess GetAuthFunc) *ApplicationStatusGetter {
    90  	return &ApplicationStatusGetter{
    91  		st:           st,
    92  		getCanAccess: getCanAccess,
    93  	}
    94  }
    95  
    96  // Status returns the status of the Application for each given Unit tag.
    97  func (s *ApplicationStatusGetter) Status(args params.Entities) (params.ApplicationStatusResults, error) {
    98  	result := params.ApplicationStatusResults{
    99  		Results: make([]params.ApplicationStatusResult, len(args.Entities)),
   100  	}
   101  	canAccess, err := s.getCanAccess()
   102  	if err != nil {
   103  		return params.ApplicationStatusResults{}, err
   104  	}
   105  
   106  	for i, arg := range args.Entities {
   107  		// TODO(fwereade): the auth is basically nonsense, and basically only
   108  		// works by coincidence (and is happening at the wrong layer anyway).
   109  		// Read carefully.
   110  
   111  		// We "know" that arg.Tag is either the calling unit or its application
   112  		// (because getCanAccess is authUnitOrApplication, and we'll fail out if
   113  		// it isn't); and, in practice, it's always going to be the calling
   114  		// unit (because, /sigh, we don't actually use application tags to refer
   115  		// to applications in this method).
   116  		tag, err := names.ParseTag(arg.Tag)
   117  		if err != nil {
   118  			result.Results[i].Error = ServerError(err)
   119  			continue
   120  		}
   121  		if !canAccess(tag) {
   122  			result.Results[i].Error = ServerError(ErrPerm)
   123  			continue
   124  		}
   125  		unitTag, ok := tag.(names.UnitTag)
   126  		if !ok {
   127  			// No matter what the canAccess says, if this entity is not
   128  			// a unit, we say "NO".
   129  			result.Results[i].Error = ServerError(ErrPerm)
   130  			continue
   131  		}
   132  		unitId := unitTag.Id()
   133  
   134  		// Now we have the unit, we can get the application that should have been
   135  		// specified in the first place...
   136  		applicationId, err := names.UnitApplication(unitId)
   137  		if err != nil {
   138  			result.Results[i].Error = ServerError(err)
   139  			continue
   140  		}
   141  		application, err := s.st.Application(applicationId)
   142  		if err != nil {
   143  			result.Results[i].Error = ServerError(err)
   144  			continue
   145  		}
   146  
   147  		// ...so we can check the unit's application leadership...
   148  		checker := s.st.LeadershipChecker()
   149  		token := checker.LeadershipCheck(applicationId, unitId)
   150  		if err := token.Check(0, nil); err != nil {
   151  			// TODO(fwereade) this should probably be ErrPerm is certain cases,
   152  			// but I don't think I implemented an exported ErrNotLeader. I
   153  			// should have done, though.
   154  			result.Results[i].Error = ServerError(err)
   155  			continue
   156  		}
   157  
   158  		// ...and collect the results.
   159  		applicationStatus, unitStatuses, err := application.ApplicationAndUnitsStatus()
   160  		if err != nil {
   161  			result.Results[i].Application.Error = ServerError(err)
   162  			result.Results[i].Error = ServerError(err)
   163  			continue
   164  		}
   165  		result.Results[i].Application.Status = applicationStatus.Status.String()
   166  		result.Results[i].Application.Info = applicationStatus.Message
   167  		result.Results[i].Application.Data = applicationStatus.Data
   168  		result.Results[i].Application.Since = applicationStatus.Since
   169  
   170  		result.Results[i].Units = make(map[string]params.StatusResult, len(unitStatuses))
   171  		for uTag, r := range unitStatuses {
   172  			ur := params.StatusResult{
   173  				Status: r.Status.String(),
   174  				Info:   r.Message,
   175  				Data:   r.Data,
   176  				Since:  r.Since,
   177  			}
   178  			result.Results[i].Units[uTag] = ur
   179  		}
   180  	}
   181  	return result, nil
   182  }
   183  
   184  // EntityStatusFromState converts a state.StatusInfo into a params.EntityStatus.
   185  func EntityStatusFromState(statusInfo status.StatusInfo) params.EntityStatus {
   186  	return params.EntityStatus{
   187  		Status: statusInfo.Status,
   188  		Info:   statusInfo.Message,
   189  		Data:   statusInfo.Data,
   190  		Since:  statusInfo.Since,
   191  	}
   192  }