github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/leadership/settings.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package leadership 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/core/leadership" 14 ) 15 16 // NewLeadershipSettingsAccessor creates a new 17 // LeadershipSettingsAccessor. 18 func NewLeadershipSettingsAccessor( 19 authorizer facade.Authorizer, 20 registerWatcherFn RegisterWatcherFn, 21 getSettingsFn GetSettingsFn, 22 leaderCheckFn LeaderCheckFn, 23 mergeSettingsChunkFn MergeSettingsChunkFn, 24 ) *LeadershipSettingsAccessor { 25 26 return &LeadershipSettingsAccessor{ 27 authorizer: authorizer, 28 registerWatcherFn: registerWatcherFn, 29 getSettingsFn: getSettingsFn, 30 leaderCheckFn: leaderCheckFn, 31 mergeSettingsChunkFn: mergeSettingsChunkFn, 32 } 33 } 34 35 // SettingsChangeNotifierFn declares a function-type which will return 36 // a channel that can be blocked on to be notified of setting changes 37 // for the provided document key. 38 type RegisterWatcherFn func(serviceId string) (watcherId string, _ error) 39 40 // GetSettingsFn declares a function-type which will return leadership 41 // settings for the given service ID. 42 type GetSettingsFn func(serviceId string) (map[string]string, error) 43 44 // LeaderCheckFn returns a Token whose Check method will return an error 45 // if the unit is not leader of the service. 46 type LeaderCheckFn func(serviceId, unitId string) leadership.Token 47 48 // MergeSettingsChunk declares a function-type which will write the 49 // provided settings chunk into the greater leadership settings for 50 // the provided service ID, so long as the supplied Token remains 51 // valid. 52 type MergeSettingsChunkFn func(token leadership.Token, serviceId string, settings map[string]string) error 53 54 // LeadershipSettingsAccessor provides a type which can read, write, 55 // and watch leadership settings. 56 type LeadershipSettingsAccessor struct { 57 authorizer facade.Authorizer 58 registerWatcherFn RegisterWatcherFn 59 getSettingsFn GetSettingsFn 60 leaderCheckFn LeaderCheckFn 61 mergeSettingsChunkFn MergeSettingsChunkFn 62 } 63 64 func (lsa *LeadershipSettingsAccessor) callerApplication() (string, error) { 65 var appName string 66 switch authTag := lsa.authorizer.GetAuthTag().(type) { 67 case names.UnitTag: 68 var err error 69 appName, err = names.UnitApplication(authTag.Id()) 70 if err != nil { 71 return "", err 72 } 73 case names.ApplicationTag: 74 appName = authTag.Id() 75 default: 76 return "", errors.Errorf("invalid auth tag type %T: %v", authTag, authTag.String()) 77 } 78 return appName, nil 79 } 80 81 // Merge merges in the provided leadership settings. Only leaders for 82 // the given service may perform this operation. 83 func (lsa *LeadershipSettingsAccessor) Merge(bulkArgs params.MergeLeadershipSettingsBulkParams) (params.ErrorResults, error) { 84 85 requireAppName, err := lsa.callerApplication() 86 if err != nil { 87 return params.ErrorResults{}, err 88 } 89 // Start out assuming the caller is a unit (for older clients). 90 callerUnitId := lsa.authorizer.GetAuthTag().Id() 91 92 results := make([]params.ErrorResult, len(bulkArgs.Params)) 93 94 for i, arg := range bulkArgs.Params { 95 result := &results[i] 96 97 // TODO(fwereade): we shoudn't assume a ApplicationTag: we should 98 // use an actual auth func to determine permissions. 99 applicationTag, err := names.ParseApplicationTag(arg.ApplicationTag) 100 if err != nil { 101 result.Error = common.ServerError(err) 102 continue 103 } 104 105 // If a unit is passed in as an arg, use that instead of the caller id. 106 if arg.UnitTag != "" { 107 unitTag, err := names.ParseUnitTag(arg.UnitTag) 108 if err != nil { 109 result.Error = common.ServerError(err) 110 continue 111 } 112 callerUnitId = unitTag.Id() 113 unitAppName, err := names.UnitApplication(callerUnitId) 114 if err != nil || unitAppName != requireAppName { 115 result.Error = common.ServerError(common.ErrPerm) 116 continue 117 } 118 } 119 120 appName := applicationTag.Id() 121 if appName != requireAppName { 122 result.Error = common.ServerError(common.ErrPerm) 123 continue 124 } 125 126 token := lsa.leaderCheckFn(appName, callerUnitId) 127 err = lsa.mergeSettingsChunkFn(token, appName, arg.Settings) 128 if err != nil { 129 result.Error = common.ServerError(err) 130 } 131 } 132 133 return params.ErrorResults{Results: results}, nil 134 } 135 136 // Read reads leadership settings for the provided service ID. Any 137 // unit of the service may perform this operation. 138 func (lsa *LeadershipSettingsAccessor) Read(bulkArgs params.Entities) (params.GetLeadershipSettingsBulkResults, error) { 139 140 requireAppName, err := lsa.callerApplication() 141 if err != nil { 142 return params.GetLeadershipSettingsBulkResults{}, err 143 } 144 results := make([]params.GetLeadershipSettingsResult, len(bulkArgs.Entities)) 145 146 for i, arg := range bulkArgs.Entities { 147 result := &results[i] 148 149 // TODO(fwereade): we shoudn't assume a ApplicationTag: we should 150 // use an actual auth func to determine permissions. 151 applicationTag, err := names.ParseApplicationTag(arg.Tag) 152 if err != nil { 153 result.Error = common.ServerError(err) 154 continue 155 } 156 157 appName := applicationTag.Id() 158 if appName != requireAppName { 159 result.Error = common.ServerError(common.ErrPerm) 160 continue 161 } 162 163 settings, err := lsa.getSettingsFn(appName) 164 if err != nil { 165 result.Error = common.ServerError(err) 166 continue 167 } 168 169 result.Settings = settings 170 } 171 172 return params.GetLeadershipSettingsBulkResults{results}, nil 173 } 174 175 // WatchLeadershipSettings will block the caller until leadership settings 176 // for the given service ID change. 177 func (lsa *LeadershipSettingsAccessor) WatchLeadershipSettings(bulkArgs params.Entities) (params.NotifyWatchResults, error) { 178 179 requireAppName, err := lsa.callerApplication() 180 if err != nil { 181 return params.NotifyWatchResults{}, err 182 } 183 results := make([]params.NotifyWatchResult, len(bulkArgs.Entities)) 184 185 for i, arg := range bulkArgs.Entities { 186 result := &results[i] 187 188 // TODO(fwereade): we shoudn't assume a ApplicationTag: we should 189 // use an actual auth func to determine permissions. 190 applicationTag, err := names.ParseApplicationTag(arg.Tag) 191 if err != nil { 192 result.Error = common.ServerError(err) 193 continue 194 } 195 196 appName := applicationTag.Id() 197 if appName != requireAppName { 198 result.Error = common.ServerError(common.ErrPerm) 199 continue 200 } 201 202 watcherId, err := lsa.registerWatcherFn(appName) 203 if err != nil { 204 result.Error = common.ServerError(err) 205 continue 206 } 207 208 result.NotifyWatcherId = watcherId 209 } 210 return params.NotifyWatchResults{Results: results}, nil 211 }