volumes.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package cluster // import "github.com/docker/docker/daemon/cluster"
  2. import (
  3. "context"
  4. "fmt"
  5. volumetypes "github.com/docker/docker/api/types/volume"
  6. "github.com/docker/docker/daemon/cluster/convert"
  7. "github.com/docker/docker/errdefs"
  8. swarmapi "github.com/moby/swarmkit/v2/api"
  9. "google.golang.org/grpc"
  10. )
  11. // GetVolume returns a volume from the swarm cluster.
  12. func (c *Cluster) GetVolume(nameOrID string) (volumetypes.Volume, error) {
  13. var volume *swarmapi.Volume
  14. if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
  15. v, err := getVolume(ctx, state.controlClient, nameOrID)
  16. if err != nil {
  17. return err
  18. }
  19. volume = v
  20. return nil
  21. }); err != nil {
  22. return volumetypes.Volume{}, err
  23. }
  24. return convert.VolumeFromGRPC(volume), nil
  25. }
  26. // GetVolumes returns all of the volumes matching the given options from a swarm cluster.
  27. func (c *Cluster) GetVolumes(options volumetypes.ListOptions) ([]*volumetypes.Volume, error) {
  28. var volumes []*volumetypes.Volume
  29. if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
  30. r, err := state.controlClient.ListVolumes(
  31. ctx, &swarmapi.ListVolumesRequest{},
  32. grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse),
  33. )
  34. if err != nil {
  35. return err
  36. }
  37. volumes = make([]*volumetypes.Volume, 0, len(r.Volumes))
  38. for _, volume := range r.Volumes {
  39. v := convert.VolumeFromGRPC(volume)
  40. volumes = append(volumes, &v)
  41. }
  42. return nil
  43. }); err != nil {
  44. return nil, err
  45. }
  46. return volumes, nil
  47. }
  48. // CreateVolume creates a new cluster volume in the swarm cluster.
  49. //
  50. // Returns the volume ID if creation is successful, or an error if not.
  51. func (c *Cluster) CreateVolume(v volumetypes.CreateOptions) (*volumetypes.Volume, error) {
  52. var resp *swarmapi.CreateVolumeResponse
  53. if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
  54. volumeSpec := convert.VolumeCreateToGRPC(&v)
  55. r, err := state.controlClient.CreateVolume(
  56. ctx, &swarmapi.CreateVolumeRequest{Spec: volumeSpec},
  57. )
  58. if err != nil {
  59. return err
  60. }
  61. resp = r
  62. return nil
  63. }); err != nil {
  64. return nil, err
  65. }
  66. createdVol, err := c.GetVolume(resp.Volume.ID)
  67. if err != nil {
  68. // If there's a failure of some sort in this operation the user would
  69. // get a very unhelpful "not found" error on a create, which is not
  70. // very helpful at all. Instead, before returning the error, add some
  71. // context, and change this to a system-type error, because it's
  72. // nothing the user did wrong.
  73. return nil, errdefs.System(fmt.Errorf("unable to retrieve created volume: %w", err))
  74. }
  75. return &createdVol, nil
  76. }
  77. // RemoveVolume removes a volume from the swarm cluster.
  78. func (c *Cluster) RemoveVolume(nameOrID string, force bool) error {
  79. return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
  80. volume, err := getVolume(ctx, state.controlClient, nameOrID)
  81. if err != nil {
  82. if force && errdefs.IsNotFound(err) {
  83. return nil
  84. }
  85. return err
  86. }
  87. req := &swarmapi.RemoveVolumeRequest{
  88. VolumeID: volume.ID,
  89. Force: force,
  90. }
  91. _, err = state.controlClient.RemoveVolume(ctx, req)
  92. return err
  93. })
  94. }
  95. // UpdateVolume updates a volume in the swarm cluster.
  96. func (c *Cluster) UpdateVolume(nameOrID string, version uint64, volume volumetypes.UpdateOptions) error {
  97. return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
  98. v, err := getVolume(ctx, state.controlClient, nameOrID)
  99. if err != nil {
  100. return err
  101. }
  102. // For now, the only thing we can update is availability. Instead of
  103. // converting the whole spec, just pluck out the availability if it has
  104. // been set.
  105. if volume.Spec != nil {
  106. switch volume.Spec.Availability {
  107. case volumetypes.AvailabilityActive:
  108. v.Spec.Availability = swarmapi.VolumeAvailabilityActive
  109. case volumetypes.AvailabilityPause:
  110. v.Spec.Availability = swarmapi.VolumeAvailabilityPause
  111. case volumetypes.AvailabilityDrain:
  112. v.Spec.Availability = swarmapi.VolumeAvailabilityDrain
  113. }
  114. // if default empty value, change nothing.
  115. }
  116. _, err = state.controlClient.UpdateVolume(
  117. ctx, &swarmapi.UpdateVolumeRequest{
  118. VolumeID: nameOrID,
  119. VolumeVersion: &swarmapi.Version{
  120. Index: version,
  121. },
  122. Spec: &v.Spec,
  123. },
  124. )
  125. return err
  126. })
  127. }