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