dotcloud/docker

View on GitHub
volume/service/convert.go

Summary

Maintainability
A
0 mins
Test Coverage
package service

import (
    "context"
    "fmt"
    "strconv"
    "time"

    "github.com/containerd/log"
    "github.com/docker/docker/api/types/filters"
    volumetypes "github.com/docker/docker/api/types/volume"
    "github.com/docker/docker/errdefs"
    "github.com/docker/docker/internal/directory"
    "github.com/docker/docker/volume"
)

// 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
}