Files
moby/daemon/volume/service/convert.go
Cory Snider 778e5bfad3 api/types/filters: move to daemon/internal
Most of the code in the filters package relates to the unmarshaling,
validation and application of filters from client requests. None of this
is necessary or particularly useful for Go SDK users. Move the full-fat
filters package into daemon/internal and switch all the daemon code to
import that package so we are free to iterate upon the code without
worrying about source-code interface compatibility.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2025-10-08 12:06:26 -04:00

153 lines
3.2 KiB
Go

package service
import (
"context"
"fmt"
"strconv"
"time"
"github.com/containerd/log"
volumetypes "github.com/moby/moby/api/types/volume"
"github.com/moby/moby/v2/daemon/internal/directory"
"github.com/moby/moby/v2/daemon/internal/filters"
"github.com/moby/moby/v2/daemon/volume"
"github.com/moby/moby/v2/errdefs"
)
// convertOpts are used to pass options to `volumeToAPI`
type convertOpt interface {
isConvertOpt()
}
type useCachedPath bool
func (useCachedPath) isConvertOpt() {}
type calcSize bool
func (calcSize) isConvertOpt() {}
type pathCacher interface {
CachedPath() string
}
func (s *VolumesService) volumesToAPI(ctx context.Context, volumes []volume.Volume, opts ...convertOpt) []*volumetypes.Volume {
var (
out = make([]*volumetypes.Volume, 0, len(volumes))
getSize bool
cachedPath bool
)
for _, o := range opts {
switch t := o.(type) {
case calcSize:
getSize = bool(t)
case useCachedPath:
cachedPath = bool(t)
}
}
for _, v := range volumes {
select {
case <-ctx.Done():
return nil
default:
}
apiV := volumeToAPIType(v)
if cachedPath {
if vv, ok := v.(pathCacher); ok {
apiV.Mountpoint = vv.CachedPath()
}
} else {
apiV.Mountpoint = v.Path()
}
if getSize {
p := v.Path()
if apiV.Mountpoint == "" {
apiV.Mountpoint = p
}
sz, err := directory.Size(ctx, p)
if err != nil {
log.G(ctx).WithError(err).WithField("volume", v.Name()).Warnf("Failed to determine size of volume")
sz = -1
}
apiV.UsageData = &volumetypes.UsageData{Size: sz, RefCount: int64(s.vs.CountReferences(v))}
}
out = append(out, &apiV)
}
return out
}
func volumeToAPIType(v volume.Volume) volumetypes.Volume {
createdAt, _ := v.CreatedAt()
tv := volumetypes.Volume{
Name: v.Name(),
Driver: v.DriverName(),
CreatedAt: createdAt.Format(time.RFC3339),
}
if v, ok := v.(volume.DetailedVolume); ok {
tv.Labels = v.Labels()
tv.Options = v.Options()
tv.Scope = v.Scope()
}
if cp, ok := v.(pathCacher); ok {
tv.Mountpoint = cp.CachedPath()
}
return tv
}
func filtersToBy(filter filters.Args, acceptedFilters map[string]bool) (By, error) {
if err := filter.Validate(acceptedFilters); err != nil {
return nil, err
}
var bys []By
if drivers := filter.Get("driver"); len(drivers) > 0 {
bys = append(bys, ByDriver(drivers...))
}
if filter.Contains("name") {
bys = append(bys, CustomFilter(func(v volume.Volume) bool {
return filter.Match("name", v.Name())
}))
}
bys = append(bys, byLabelFilter(filter))
if filter.Contains("dangling") {
dangling, err := filter.GetBoolOrDefault("dangling", false)
if err != nil {
return nil, err
}
bys = append(bys, ByReferenced(!dangling))
}
var by By
switch len(bys) {
case 0:
case 1:
by = bys[0]
default:
by = And(bys...)
}
return by, nil
}
func withPrune(filter filters.Args) error {
all := filter.Get("all")
switch {
case len(all) > 1:
return errdefs.InvalidParameter(fmt.Errorf("invalid filter 'all=%s': only one value is expected", all))
case len(all) == 1:
ok, err := strconv.ParseBool(all[0])
if err != nil {
return errdefs.InvalidParameter(fmt.Errorf("invalid filter 'all': %w", err))
}
if ok {
return nil
}
}
filter.Add("label", AnonymousLabel)
return nil
}