plugin_spec.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package xfer
  2. import (
  3. "bytes"
  4. "fmt"
  5. "sort"
  6. "github.com/davecgh/go-spew/spew"
  7. "github.com/ugorji/go/codec"
  8. "github.com/weaveworks/ps"
  9. "github.com/weaveworks/scope/test/reflect"
  10. )
  11. // PluginSpec is shared between the Probe, App, and UI. It is the plugin's
  12. // self-proclaimed description.
  13. type PluginSpec struct {
  14. ID string `json:"id"`
  15. // Label is a human-readable name of the plugin
  16. Label string `json:"label"`
  17. Description string `json:"description,omitempty"`
  18. // Interfaces is a list of things this plugin can be used for (e.g. "reporter")
  19. Interfaces []string `json:"interfaces"`
  20. APIVersion string `json:"api_version,omitempty"`
  21. Status string `json:"status,omitempty"`
  22. }
  23. // PluginSpecs is a set of plugin specs keyed on ID. Clients must use
  24. // the Add method to add plugin specs
  25. type PluginSpecs struct {
  26. psMap ps.Map
  27. }
  28. // EmptyPluginSpecs is the empty set of plugin specs.
  29. var EmptyPluginSpecs = PluginSpecs{ps.NewMap()}
  30. // MakePluginSpecs makes a new PluginSpecs with the given plugin specs.
  31. func MakePluginSpecs(specs ...PluginSpec) PluginSpecs {
  32. return EmptyPluginSpecs.Add(specs...)
  33. }
  34. // Add adds the specs to the PluginSpecs. Add is the only valid way to grow a
  35. // PluginSpecs. Add returns the PluginSpecs to enable chaining.
  36. func (n PluginSpecs) Add(specs ...PluginSpec) PluginSpecs {
  37. result := n.psMap
  38. if result == nil {
  39. result = ps.NewMap()
  40. }
  41. for _, spec := range specs {
  42. result = result.Set(spec.ID, spec)
  43. }
  44. return PluginSpecs{result}
  45. }
  46. // Merge combines the two PluginSpecss and returns a new result.
  47. func (n PluginSpecs) Merge(other PluginSpecs) PluginSpecs {
  48. nSize, otherSize := n.Size(), other.Size()
  49. if nSize == 0 {
  50. return other
  51. }
  52. if otherSize == 0 {
  53. return n
  54. }
  55. result, iter := n.psMap, other.psMap
  56. if nSize < otherSize {
  57. result, iter = iter, result
  58. }
  59. iter.ForEach(func(key string, otherVal interface{}) {
  60. result = result.Set(key, otherVal)
  61. })
  62. return PluginSpecs{result}
  63. }
  64. // Lookup the spec by 'key'
  65. func (n PluginSpecs) Lookup(key string) (PluginSpec, bool) {
  66. if n.psMap != nil {
  67. value, ok := n.psMap.Lookup(key)
  68. if ok {
  69. return value.(PluginSpec), true
  70. }
  71. }
  72. return PluginSpec{}, false
  73. }
  74. // Keys is a list of all the keys in this set.
  75. func (n PluginSpecs) Keys() []string {
  76. if n.psMap == nil {
  77. return nil
  78. }
  79. k := n.psMap.Keys()
  80. sort.Strings(k)
  81. return k
  82. }
  83. // Size is the number of specs in the set
  84. func (n PluginSpecs) Size() int {
  85. if n.psMap == nil {
  86. return 0
  87. }
  88. return n.psMap.Size()
  89. }
  90. // ForEach executes f for each spec in the set. Nodes are traversed in sorted
  91. // order.
  92. func (n PluginSpecs) ForEach(f func(PluginSpec)) {
  93. for _, key := range n.Keys() {
  94. if val, ok := n.psMap.Lookup(key); ok {
  95. f(val.(PluginSpec))
  96. }
  97. }
  98. }
  99. // Copy is a noop
  100. func (n PluginSpecs) Copy() PluginSpecs {
  101. return n
  102. }
  103. func (n PluginSpecs) String() string {
  104. keys := []string{}
  105. if n.psMap == nil {
  106. n = EmptyPluginSpecs
  107. }
  108. psMap := n.psMap
  109. if psMap == nil {
  110. psMap = ps.NewMap()
  111. }
  112. for _, k := range psMap.Keys() {
  113. keys = append(keys, k)
  114. }
  115. sort.Strings(keys)
  116. buf := bytes.NewBufferString("{")
  117. for _, key := range keys {
  118. val, _ := psMap.Lookup(key)
  119. fmt.Fprintf(buf, "%s: %s, ", key, spew.Sdump(val))
  120. }
  121. fmt.Fprintf(buf, "}")
  122. return buf.String()
  123. }
  124. // DeepEqual tests equality with other PluginSpecss
  125. func (n PluginSpecs) DeepEqual(i interface{}) bool {
  126. d, ok := i.(PluginSpecs)
  127. if !ok {
  128. return false
  129. }
  130. if n.Size() != d.Size() {
  131. return false
  132. }
  133. if n.Size() == 0 {
  134. return true
  135. }
  136. equal := true
  137. n.psMap.ForEach(func(k string, val interface{}) {
  138. if otherValue, ok := d.psMap.Lookup(k); !ok {
  139. equal = false
  140. } else {
  141. equal = equal && reflect.DeepEqual(val, otherValue)
  142. }
  143. })
  144. return equal
  145. }
  146. func (n PluginSpecs) toIntermediate() []PluginSpec {
  147. intermediate := make([]PluginSpec, 0, n.Size())
  148. n.ForEach(func(spec PluginSpec) {
  149. intermediate = append(intermediate, spec)
  150. })
  151. return intermediate
  152. }
  153. func (n PluginSpecs) fromIntermediate(specs []PluginSpec) PluginSpecs {
  154. return MakePluginSpecs(specs...)
  155. }
  156. // CodecEncodeSelf implements codec.Selfer
  157. func (n *PluginSpecs) CodecEncodeSelf(encoder *codec.Encoder) {
  158. if n.psMap != nil {
  159. encoder.Encode(n.toIntermediate())
  160. } else {
  161. encoder.Encode(nil)
  162. }
  163. }
  164. // CodecDecodeSelf implements codec.Selfer
  165. func (n *PluginSpecs) CodecDecodeSelf(decoder *codec.Decoder) {
  166. in := []PluginSpec{}
  167. if err := decoder.Decode(&in); err != nil {
  168. return
  169. }
  170. *n = PluginSpecs{}.fromIntermediate(in)
  171. }
  172. // MarshalJSON shouldn't be used, use CodecEncodeSelf instead
  173. func (PluginSpecs) MarshalJSON() ([]byte, error) {
  174. panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
  175. }
  176. // UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
  177. func (*PluginSpecs) UnmarshalJSON(b []byte) error {
  178. panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
  179. }
  180. // PluginSpecsByID implements sort.Interface, so we can sort the specs by the
  181. // ID field.
  182. type PluginSpecsByID []PluginSpec
  183. // Len is part of sort.Interface.
  184. func (p PluginSpecsByID) Len() int {
  185. return len(p)
  186. }
  187. // Swap is part of sort.Interface.
  188. func (p PluginSpecsByID) Swap(i, j int) {
  189. p[i], p[j] = p[j], p[i]
  190. }
  191. // Less is part of sort.Interface.
  192. func (p PluginSpecsByID) Less(i, j int) bool {
  193. return p[i].ID < p[j].ID
  194. }