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  }