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 }