configuration.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package main
  2. import (
  3. "reflect"
  4. "github.com/pkg/errors"
  5. )
  6. // configuration captures the plugin's external configuration as exposed in the Mattermost server
  7. // configuration, as well as values computed from the configuration. Any public fields will be
  8. // deserialized from the Mattermost server configuration in OnConfigurationChange.
  9. //
  10. // As plugins are inherently concurrent (hooks being called asynchronously), and the plugin
  11. // configuration can change at any time, access to the configuration must be synchronized. The
  12. // strategy used in this plugin is to guard a pointer to the configuration, and clone the entire
  13. // struct whenever it changes. You may replace this with whatever strategy you choose.
  14. //
  15. // If you add non-reference types to your configuration struct, be sure to rewrite Clone as a deep
  16. // copy appropriate for your types.
  17. type configuration struct {
  18. }
  19. // Clone shallow copies the configuration. Your implementation may require a deep copy if
  20. // your configuration has reference types.
  21. func (c *configuration) Clone() *configuration {
  22. var clone = *c
  23. return &clone
  24. }
  25. // getConfiguration retrieves the active configuration under lock, making it safe to use
  26. // concurrently. The active configuration may change underneath the client of this method, but
  27. // the struct returned by this API call is considered immutable.
  28. func (p *Plugin) getConfiguration() *configuration {
  29. p.configurationLock.RLock()
  30. defer p.configurationLock.RUnlock()
  31. if p.configuration == nil {
  32. return &configuration{}
  33. }
  34. return p.configuration
  35. }
  36. // setConfiguration replaces the active configuration under lock.
  37. //
  38. // Do not call setConfiguration while holding the configurationLock, as sync.Mutex is not
  39. // reentrant. In particular, avoid using the plugin API entirely, as this may in turn trigger a
  40. // hook back into the plugin. If that hook attempts to acquire this lock, a deadlock may occur.
  41. //
  42. // This method panics if setConfiguration is called with the existing configuration. This almost
  43. // certainly means that the configuration was modified without being cloned and may result in
  44. // an unsafe access.
  45. func (p *Plugin) setConfiguration(configuration *configuration) {
  46. p.configurationLock.Lock()
  47. defer p.configurationLock.Unlock()
  48. if configuration != nil && p.configuration == configuration {
  49. // Ignore assignment if the configuration struct is empty. Go will optimize the
  50. // allocation for same to point at the same memory address, breaking the check
  51. // above.
  52. if reflect.ValueOf(*configuration).NumField() == 0 {
  53. return
  54. }
  55. panic("setConfiguration called with the existing configuration")
  56. }
  57. p.configuration = configuration
  58. }
  59. // OnConfigurationChange is invoked when configuration changes may have been made.
  60. func (p *Plugin) OnConfigurationChange() error {
  61. var configuration = new(configuration)
  62. // Load the public configuration fields from the Mattermost server configuration.
  63. if err := p.API.LoadPluginConfiguration(configuration); err != nil {
  64. return errors.Wrap(err, "failed to load plugin configuration")
  65. }
  66. p.setConfiguration(configuration)
  67. return nil
  68. }