testutils.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. package testutils // import "github.com/docker/docker/volume/testutils"
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "net/http"
  8. "time"
  9. "github.com/docker/docker/pkg/plugingetter"
  10. "github.com/docker/docker/pkg/plugins"
  11. "github.com/docker/docker/volume"
  12. )
  13. // NoopVolume is a volume that doesn't perform any operation
  14. type NoopVolume struct{}
  15. // Name is the name of the volume
  16. func (NoopVolume) Name() string { return "noop" }
  17. // DriverName is the name of the driver
  18. func (NoopVolume) DriverName() string { return "noop" }
  19. // Path is the filesystem path to the volume
  20. func (NoopVolume) Path() string { return "noop" }
  21. // Mount mounts the volume in the container
  22. func (NoopVolume) Mount(_ string) (string, error) { return "noop", nil }
  23. // Unmount unmounts the volume from the container
  24. func (NoopVolume) Unmount(_ string) error { return nil }
  25. // Status provides low-level details about the volume
  26. func (NoopVolume) Status() map[string]interface{} { return nil }
  27. // CreatedAt provides the time the volume (directory) was created at
  28. func (NoopVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }
  29. // FakeVolume is a fake volume with a random name
  30. type FakeVolume struct {
  31. name string
  32. driverName string
  33. createdAt time.Time
  34. }
  35. // NewFakeVolume creates a new fake volume for testing
  36. func NewFakeVolume(name string, driverName string) volume.Volume {
  37. return FakeVolume{name: name, driverName: driverName, createdAt: time.Now()}
  38. }
  39. // Name is the name of the volume
  40. func (f FakeVolume) Name() string { return f.name }
  41. // DriverName is the name of the driver
  42. func (f FakeVolume) DriverName() string { return f.driverName }
  43. // Path is the filesystem path to the volume
  44. func (FakeVolume) Path() string { return "fake" }
  45. // Mount mounts the volume in the container
  46. func (FakeVolume) Mount(_ string) (string, error) { return "fake", nil }
  47. // Unmount unmounts the volume from the container
  48. func (FakeVolume) Unmount(_ string) error { return nil }
  49. // Status provides low-level details about the volume
  50. func (FakeVolume) Status() map[string]interface{} {
  51. return map[string]interface{}{"datakey": "datavalue"}
  52. }
  53. // CreatedAt provides the time the volume (directory) was created at
  54. func (f FakeVolume) CreatedAt() (time.Time, error) {
  55. return f.createdAt, nil
  56. }
  57. // FakeDriver is a driver that generates fake volumes
  58. type FakeDriver struct {
  59. name string
  60. vols map[string]volume.Volume
  61. }
  62. // NewFakeDriver creates a new FakeDriver with the specified name
  63. func NewFakeDriver(name string) volume.Driver {
  64. return &FakeDriver{
  65. name: name,
  66. vols: make(map[string]volume.Volume),
  67. }
  68. }
  69. // Name is the name of the driver
  70. func (d *FakeDriver) Name() string { return d.name }
  71. // Create initializes a fake volume.
  72. // It returns an error if the options include an "error" key with a message
  73. func (d *FakeDriver) Create(name string, opts map[string]string) (volume.Volume, error) {
  74. if opts != nil && opts["error"] != "" {
  75. return nil, fmt.Errorf(opts["error"])
  76. }
  77. v := NewFakeVolume(name, d.name)
  78. d.vols[name] = v
  79. return v, nil
  80. }
  81. // Remove deletes a volume.
  82. func (d *FakeDriver) Remove(v volume.Volume) error {
  83. if _, exists := d.vols[v.Name()]; !exists {
  84. return fmt.Errorf("no such volume")
  85. }
  86. delete(d.vols, v.Name())
  87. return nil
  88. }
  89. // List lists the volumes
  90. func (d *FakeDriver) List() ([]volume.Volume, error) {
  91. var vols []volume.Volume
  92. for _, v := range d.vols {
  93. vols = append(vols, v)
  94. }
  95. return vols, nil
  96. }
  97. // Get gets the volume
  98. func (d *FakeDriver) Get(name string) (volume.Volume, error) {
  99. if v, exists := d.vols[name]; exists {
  100. return v, nil
  101. }
  102. return nil, fmt.Errorf("no such volume")
  103. }
  104. // Scope returns the local scope
  105. func (*FakeDriver) Scope() string {
  106. return "local"
  107. }
  108. type fakePlugin struct {
  109. client *plugins.Client
  110. name string
  111. refs int
  112. }
  113. // MakeFakePlugin creates a fake plugin from the passed in driver
  114. // Note: currently only "Create" is implemented because that's all that's needed
  115. // so far. If you need it to test something else, add it here, but probably you
  116. // shouldn't need to use this except for very specific cases with v2 plugin handling.
  117. func MakeFakePlugin(d volume.Driver, l net.Listener) (plugingetter.CompatPlugin, error) {
  118. c, err := plugins.NewClient(l.Addr().Network()+"://"+l.Addr().String(), nil)
  119. if err != nil {
  120. return nil, err
  121. }
  122. mux := http.NewServeMux()
  123. mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
  124. createReq := struct {
  125. Name string
  126. Opts map[string]string
  127. }{}
  128. if err := json.NewDecoder(r.Body).Decode(&createReq); err != nil {
  129. fmt.Fprintf(w, `{"Err": "%s"}`, err.Error())
  130. return
  131. }
  132. _, err := d.Create(createReq.Name, createReq.Opts)
  133. if err != nil {
  134. fmt.Fprintf(w, `{"Err": "%s"}`, err.Error())
  135. return
  136. }
  137. w.Write([]byte("{}"))
  138. })
  139. go http.Serve(l, mux) // #nosec G114 -- Ignoring for test-code: G114: Use of net/http serve function that has no support for setting timeouts (gosec)
  140. return &fakePlugin{client: c, name: d.Name()}, nil
  141. }
  142. func (p *fakePlugin) Client() *plugins.Client {
  143. return p.client
  144. }
  145. func (p *fakePlugin) Name() string {
  146. return p.name
  147. }
  148. func (p *fakePlugin) IsV1() bool {
  149. return false
  150. }
  151. func (p *fakePlugin) ScopedPath(s string) string {
  152. return s
  153. }
  154. type fakePluginGetter struct {
  155. plugins map[string]plugingetter.CompatPlugin
  156. }
  157. // NewFakePluginGetter returns a plugin getter for fake plugins
  158. func NewFakePluginGetter(pls ...plugingetter.CompatPlugin) plugingetter.PluginGetter {
  159. idx := make(map[string]plugingetter.CompatPlugin, len(pls))
  160. for _, p := range pls {
  161. idx[p.Name()] = p
  162. }
  163. return &fakePluginGetter{plugins: idx}
  164. }
  165. // This ignores the second argument since we only care about volume drivers here,
  166. // there shouldn't be any other kind of plugin in here
  167. func (g *fakePluginGetter) Get(name, _ string, mode int) (plugingetter.CompatPlugin, error) {
  168. p, ok := g.plugins[name]
  169. if !ok {
  170. return nil, errors.New("not found")
  171. }
  172. p.(*fakePlugin).refs += mode
  173. return p, nil
  174. }
  175. func (g *fakePluginGetter) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
  176. panic("GetAllByCap shouldn't be called")
  177. }
  178. func (g *fakePluginGetter) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin {
  179. panic("GetAllManagedPluginsByCap should not be called")
  180. }
  181. func (g *fakePluginGetter) Handle(capability string, callback func(string, *plugins.Client)) {
  182. panic("Handle should not be called")
  183. }
  184. // FakeRefs checks ref count on a fake plugin.
  185. func FakeRefs(p plugingetter.CompatPlugin) int {
  186. // this should panic if something other than a `*fakePlugin` is passed in
  187. return p.(*fakePlugin).refs
  188. }