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