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