github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/volume/service/service.go (about) 1 package service // import "github.com/docker/docker/volume/service" 2 3 import ( 4 "context" 5 "strconv" 6 "sync/atomic" 7 8 "github.com/docker/docker/api/types" 9 "github.com/docker/docker/api/types/filters" 10 "github.com/docker/docker/errdefs" 11 "github.com/docker/docker/pkg/directory" 12 "github.com/docker/docker/pkg/idtools" 13 "github.com/docker/docker/pkg/plugingetter" 14 "github.com/docker/docker/pkg/stringid" 15 "github.com/docker/docker/volume" 16 "github.com/docker/docker/volume/drivers" 17 "github.com/docker/docker/volume/service/opts" 18 "github.com/pkg/errors" 19 "github.com/sirupsen/logrus" 20 ) 21 22 type ds interface { 23 GetDriverList() []string 24 } 25 26 // VolumeEventLogger interface provides methods to log volume-related events 27 type VolumeEventLogger interface { 28 // LogVolumeEvent generates an event related to a volume. 29 LogVolumeEvent(volumeID, action string, attributes map[string]string) 30 } 31 32 // VolumesService manages access to volumes 33 // This is used as the main access point for volumes to higher level services and the API. 34 type VolumesService struct { 35 vs *VolumeStore 36 ds ds 37 pruneRunning int32 38 eventLogger VolumeEventLogger 39 } 40 41 // NewVolumeService creates a new volume service 42 func NewVolumeService(root string, pg plugingetter.PluginGetter, rootIDs idtools.Identity, logger VolumeEventLogger) (*VolumesService, error) { 43 ds := drivers.NewStore(pg) 44 if err := setupDefaultDriver(ds, root, rootIDs); err != nil { 45 return nil, err 46 } 47 48 vs, err := NewStore(root, ds, WithEventLogger(logger)) 49 if err != nil { 50 return nil, err 51 } 52 return &VolumesService{vs: vs, ds: ds, eventLogger: logger}, nil 53 } 54 55 // GetDriverList gets the list of registered volume drivers 56 func (s *VolumesService) GetDriverList() []string { 57 return s.ds.GetDriverList() 58 } 59 60 // Create creates a volume 61 // If the caller is creating this volume to be consumed immediately, it is 62 // expected that the caller specifies a reference ID. 63 // This reference ID will protect this volume from removal. 64 // 65 // A good example for a reference ID is a container's ID. 66 // When whatever is going to reference this volume is removed the caller should defeference the volume by calling `Release`. 67 func (s *VolumesService) Create(ctx context.Context, name, driverName string, opts ...opts.CreateOption) (*types.Volume, error) { 68 if name == "" { 69 name = stringid.GenerateRandomID() 70 } 71 v, err := s.vs.Create(ctx, name, driverName, opts...) 72 if err != nil { 73 return nil, err 74 } 75 76 apiV := volumeToAPIType(v) 77 return &apiV, nil 78 } 79 80 // Get returns details about a volume 81 func (s *VolumesService) Get(ctx context.Context, name string, getOpts ...opts.GetOption) (*types.Volume, error) { 82 v, err := s.vs.Get(ctx, name, getOpts...) 83 if err != nil { 84 return nil, err 85 } 86 vol := volumeToAPIType(v) 87 88 var cfg opts.GetConfig 89 for _, o := range getOpts { 90 o(&cfg) 91 } 92 93 if cfg.ResolveStatus { 94 vol.Status = v.Status() 95 } 96 return &vol, nil 97 } 98 99 // Mount mounts the volume 100 // Callers should specify a uniqe reference for each Mount/Unmount pair. 101 // 102 // Example: 103 // ```go 104 // mountID := "randomString" 105 // s.Mount(ctx, vol, mountID) 106 // s.Unmount(ctx, vol, mountID) 107 // ``` 108 func (s *VolumesService) Mount(ctx context.Context, vol *types.Volume, ref string) (string, error) { 109 v, err := s.vs.Get(ctx, vol.Name, opts.WithGetDriver(vol.Driver)) 110 if err != nil { 111 if IsNotExist(err) { 112 err = errdefs.NotFound(err) 113 } 114 return "", err 115 } 116 return v.Mount(ref) 117 } 118 119 // Unmount unmounts the volume. 120 // Note that depending on the implementation, the volume may still be mounted due to other resources using it. 121 // 122 // The reference specified here should be the same reference specified during `Mount` and should be 123 // unique for each mount/unmount pair. 124 // See `Mount` documentation for an example. 125 func (s *VolumesService) Unmount(ctx context.Context, vol *types.Volume, ref string) error { 126 v, err := s.vs.Get(ctx, vol.Name, opts.WithGetDriver(vol.Driver)) 127 if err != nil { 128 if IsNotExist(err) { 129 err = errdefs.NotFound(err) 130 } 131 return err 132 } 133 return v.Unmount(ref) 134 } 135 136 // Release releases a volume reference 137 func (s *VolumesService) Release(ctx context.Context, name string, ref string) error { 138 return s.vs.Release(ctx, name, ref) 139 } 140 141 // Remove removes a volume 142 // An error is returned if the volume is still referenced. 143 func (s *VolumesService) Remove(ctx context.Context, name string, rmOpts ...opts.RemoveOption) error { 144 var cfg opts.RemoveConfig 145 for _, o := range rmOpts { 146 o(&cfg) 147 } 148 149 v, err := s.vs.Get(ctx, name) 150 if err != nil { 151 if IsNotExist(err) && cfg.PurgeOnError { 152 return nil 153 } 154 return err 155 } 156 157 err = s.vs.Remove(ctx, v, rmOpts...) 158 if IsNotExist(err) { 159 err = nil 160 } else if IsInUse(err) { 161 err = errdefs.Conflict(err) 162 } else if IsNotExist(err) && cfg.PurgeOnError { 163 err = nil 164 } 165 return err 166 } 167 168 var acceptedPruneFilters = map[string]bool{ 169 "label": true, 170 "label!": true, 171 } 172 173 var acceptedListFilters = map[string]bool{ 174 "dangling": true, 175 "name": true, 176 "driver": true, 177 "label": true, 178 } 179 180 // LocalVolumesSize gets all local volumes and fetches their size on disk 181 // Note that this intentionally skips volumes which have mount options. Typically 182 // volumes with mount options are not really local even if they are using the 183 // local driver. 184 func (s *VolumesService) LocalVolumesSize(ctx context.Context) ([]*types.Volume, error) { 185 ls, _, err := s.vs.Find(ctx, And(ByDriver(volume.DefaultDriverName), CustomFilter(func(v volume.Volume) bool { 186 dv, ok := v.(volume.DetailedVolume) 187 return ok && len(dv.Options()) == 0 188 }))) 189 if err != nil { 190 return nil, err 191 } 192 return s.volumesToAPI(ctx, ls, calcSize(true)), nil 193 } 194 195 // Prune removes (local) volumes which match the past in filter arguments. 196 // Note that this intentionally skips volumes with mount options as there would 197 // be no space reclaimed in this case. 198 func (s *VolumesService) Prune(ctx context.Context, filter filters.Args) (*types.VolumesPruneReport, error) { 199 if !atomic.CompareAndSwapInt32(&s.pruneRunning, 0, 1) { 200 return nil, errdefs.Conflict(errors.New("a prune operation is already running")) 201 } 202 defer atomic.StoreInt32(&s.pruneRunning, 0) 203 204 by, err := filtersToBy(filter, acceptedPruneFilters) 205 if err != nil { 206 return nil, err 207 } 208 ls, _, err := s.vs.Find(ctx, And(ByDriver(volume.DefaultDriverName), ByReferenced(false), by, CustomFilter(func(v volume.Volume) bool { 209 dv, ok := v.(volume.DetailedVolume) 210 return ok && len(dv.Options()) == 0 211 }))) 212 if err != nil { 213 return nil, err 214 } 215 216 rep := &types.VolumesPruneReport{VolumesDeleted: make([]string, 0, len(ls))} 217 for _, v := range ls { 218 select { 219 case <-ctx.Done(): 220 err := ctx.Err() 221 if err == context.Canceled { 222 err = nil 223 } 224 return rep, err 225 default: 226 } 227 228 vSize, err := directory.Size(ctx, v.Path()) 229 if err != nil { 230 logrus.WithField("volume", v.Name()).WithError(err).Warn("could not determine size of volume") 231 } 232 if err := s.vs.Remove(ctx, v); err != nil { 233 logrus.WithError(err).WithField("volume", v.Name()).Warnf("Could not determine size of volume") 234 continue 235 } 236 rep.SpaceReclaimed += uint64(vSize) 237 rep.VolumesDeleted = append(rep.VolumesDeleted, v.Name()) 238 } 239 s.eventLogger.LogVolumeEvent("", "prune", map[string]string{ 240 "reclaimed": strconv.FormatInt(int64(rep.SpaceReclaimed), 10), 241 }) 242 return rep, nil 243 } 244 245 // List gets the list of volumes which match the past in filters 246 // If filters is nil or empty all volumes are returned. 247 func (s *VolumesService) List(ctx context.Context, filter filters.Args) (volumesOut []*types.Volume, warnings []string, err error) { 248 by, err := filtersToBy(filter, acceptedListFilters) 249 if err != nil { 250 return nil, nil, err 251 } 252 253 volumes, warnings, err := s.vs.Find(ctx, by) 254 if err != nil { 255 return nil, nil, err 256 } 257 258 return s.volumesToAPI(ctx, volumes, useCachedPath(true)), warnings, nil 259 } 260 261 // Shutdown shuts down the image service and dependencies 262 func (s *VolumesService) Shutdown() error { 263 return s.vs.Shutdown() 264 }