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 }