github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/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/names" 8 9 "github.com/juju/juju/apiserver/common" 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/core/leadership" 12 ) 13 14 // NewLeadershipSettingsAccessor creates a new 15 // LeadershipSettingsAccessor. 16 func NewLeadershipSettingsAccessor( 17 authorizer common.Authorizer, 18 registerWatcherFn RegisterWatcherFn, 19 getSettingsFn GetSettingsFn, 20 leaderCheckFn LeaderCheckFn, 21 mergeSettingsChunkFn MergeSettingsChunkFn, 22 ) *LeadershipSettingsAccessor { 23 24 return &LeadershipSettingsAccessor{ 25 authorizer: authorizer, 26 registerWatcherFn: registerWatcherFn, 27 getSettingsFn: getSettingsFn, 28 leaderCheckFn: leaderCheckFn, 29 mergeSettingsChunkFn: mergeSettingsChunkFn, 30 } 31 } 32 33 // SettingsChangeNotifierFn declares a function-type which will return 34 // a channel that can be blocked on to be notified of setting changes 35 // for the provided document key. 36 type RegisterWatcherFn func(serviceId string) (watcherId string, _ error) 37 38 // GetSettingsFn declares a function-type which will return leadership 39 // settings for the given service ID. 40 type GetSettingsFn func(serviceId string) (map[string]string, error) 41 42 // LeaderCheckFn returns a Token whose Check method will return an error 43 // if the unit is not leader of the service. 44 type LeaderCheckFn func(serviceId, unitId string) leadership.Token 45 46 // MergeSettingsChunk declares a function-type which will write the 47 // provided settings chunk into the greater leadership settings for 48 // the provided service ID, so long as the supplied Token remains 49 // valid. 50 type MergeSettingsChunkFn func(token leadership.Token, serviceId string, settings map[string]string) error 51 52 // LeadershipSettingsAccessor provides a type which can read, write, 53 // and watch leadership settings. 54 type LeadershipSettingsAccessor struct { 55 authorizer common.Authorizer 56 registerWatcherFn RegisterWatcherFn 57 getSettingsFn GetSettingsFn 58 leaderCheckFn LeaderCheckFn 59 mergeSettingsChunkFn MergeSettingsChunkFn 60 } 61 62 // Merge merges in the provided leadership settings. Only leaders for 63 // the given service may perform this operation. 64 func (lsa *LeadershipSettingsAccessor) Merge(bulkArgs params.MergeLeadershipSettingsBulkParams) (params.ErrorResults, error) { 65 66 callerUnitId := lsa.authorizer.GetAuthTag().Id() 67 requireServiceId, err := names.UnitService(callerUnitId) 68 if err != nil { 69 return params.ErrorResults{}, err 70 } 71 results := make([]params.ErrorResult, len(bulkArgs.Params)) 72 73 for i, arg := range bulkArgs.Params { 74 result := &results[i] 75 76 // TODO(fwereade): we shoudn't assume a ServiceTag: we should 77 // use an actual auth func to determine permissions. 78 serviceTag, err := names.ParseServiceTag(arg.ServiceTag) 79 if err != nil { 80 result.Error = common.ServerError(err) 81 continue 82 } 83 84 serviceId := serviceTag.Id() 85 if serviceId != requireServiceId { 86 result.Error = common.ServerError(common.ErrPerm) 87 continue 88 } 89 90 token := lsa.leaderCheckFn(serviceId, callerUnitId) 91 err = lsa.mergeSettingsChunkFn(token, serviceId, arg.Settings) 92 if err != nil { 93 result.Error = common.ServerError(err) 94 } 95 } 96 97 return params.ErrorResults{Results: results}, nil 98 } 99 100 // Read reads leadership settings for the provided service ID. Any 101 // unit of the service may perform this operation. 102 func (lsa *LeadershipSettingsAccessor) Read(bulkArgs params.Entities) (params.GetLeadershipSettingsBulkResults, error) { 103 104 callerUnitId := lsa.authorizer.GetAuthTag().Id() 105 requireServiceId, err := names.UnitService(callerUnitId) 106 if err != nil { 107 return params.GetLeadershipSettingsBulkResults{}, err 108 } 109 results := make([]params.GetLeadershipSettingsResult, len(bulkArgs.Entities)) 110 111 for i, arg := range bulkArgs.Entities { 112 result := &results[i] 113 114 // TODO(fwereade): we shoudn't assume a ServiceTag: we should 115 // use an actual auth func to determine permissions. 116 serviceTag, err := names.ParseServiceTag(arg.Tag) 117 if err != nil { 118 result.Error = common.ServerError(err) 119 continue 120 } 121 122 serviceId := serviceTag.Id() 123 if serviceId != requireServiceId { 124 result.Error = common.ServerError(common.ErrPerm) 125 continue 126 } 127 128 settings, err := lsa.getSettingsFn(serviceId) 129 if err != nil { 130 result.Error = common.ServerError(err) 131 continue 132 } 133 134 result.Settings = settings 135 } 136 137 return params.GetLeadershipSettingsBulkResults{results}, nil 138 } 139 140 // WatchLeadershipSettings will block the caller until leadership settings 141 // for the given service ID change. 142 func (lsa *LeadershipSettingsAccessor) WatchLeadershipSettings(bulkArgs params.Entities) (params.NotifyWatchResults, error) { 143 144 callerUnitId := lsa.authorizer.GetAuthTag().Id() 145 requireServiceId, err := names.UnitService(callerUnitId) 146 if err != nil { 147 return params.NotifyWatchResults{}, err 148 } 149 results := make([]params.NotifyWatchResult, len(bulkArgs.Entities)) 150 151 for i, arg := range bulkArgs.Entities { 152 result := &results[i] 153 154 // TODO(fwereade): we shoudn't assume a ServiceTag: we should 155 // use an actual auth func to determine permissions. 156 serviceTag, err := names.ParseServiceTag(arg.Tag) 157 if err != nil { 158 result.Error = common.ServerError(err) 159 continue 160 } 161 162 serviceId := serviceTag.Id() 163 if serviceId != requireServiceId { 164 result.Error = common.ServerError(common.ErrPerm) 165 continue 166 } 167 168 watcherId, err := lsa.registerWatcherFn(serviceId) 169 if err != nil { 170 result.Error = common.ServerError(err) 171 continue 172 } 173 174 result.NotifyWatcherId = watcherId 175 } 176 return params.NotifyWatchResults{Results: results}, nil 177 }