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