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