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