mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
filters, for images: start with untagged/tagged boolean
This is a new feature and flag. (replaces the suggestion of a flag for --untagged images). The concept is to have a syntax to filter. This begins with this filtering for the 'images' subcommand, and at that only filtering for whether images are untagged. example like: docker rmi $(docker images -q --filter 'untagged=true') Docker-DCO-1.1-Signed-off-by: Vincent Batts <vbatts@redhat.com> (github: vbatts)
This commit is contained in:
parent
afb2d5de3d
commit
5f3812ec97
6 changed files with 1678 additions and 20 deletions
|
@ -26,12 +26,14 @@ import (
|
|||
"github.com/dotcloud/docker/dockerversion"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/nat"
|
||||
"github.com/dotcloud/docker/opts"
|
||||
"github.com/dotcloud/docker/pkg/signal"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/pkg/units"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/dotcloud/docker/utils/filters"
|
||||
)
|
||||
|
||||
func (cli *DockerCli) CmdHelp(args ...string) error {
|
||||
|
@ -1145,6 +1147,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format")
|
||||
flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format")
|
||||
|
||||
var flFilter opts.ListOpts
|
||||
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'tagged=false')")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -1153,11 +1158,41 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
filter := cmd.Arg(0)
|
||||
// Consolidate all filter flags, and sanity check them early.
|
||||
// They'll get process in the daemon/server.
|
||||
imageFilters := map[string]string{}
|
||||
for _, f := range flFilter.GetAll() {
|
||||
var err error
|
||||
imageFilters, err = filters.ParseFlag(f, imageFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
/*
|
||||
var (
|
||||
untagged bool
|
||||
)
|
||||
for k,v := range imageFilters {
|
||||
}
|
||||
*/
|
||||
|
||||
// seeing -all untagged images is redundant, and no point in seeing a visualization of that
|
||||
/*
|
||||
if *flUntagged && (*all || *flViz || *flTree) {
|
||||
fmt.Fprintln(cli.err, "Notice: --untagged is not to be used with --all, --tree or --viz")
|
||||
*flUntagged = false
|
||||
}
|
||||
*/
|
||||
|
||||
matchName := cmd.Arg(0)
|
||||
|
||||
// FIXME: --viz and --tree are deprecated. Remove them in a future version.
|
||||
if *flViz || *flTree {
|
||||
body, _, err := readBody(cli.call("GET", "/images/json?all=1", nil, false))
|
||||
v := url.Values{
|
||||
"all": []string{"1"},
|
||||
"filters": []string{filters.ToParam(imageFilters)},
|
||||
}
|
||||
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1187,13 +1222,13 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
}
|
||||
}
|
||||
|
||||
if filter != "" {
|
||||
if filter == image.Get("Id") || filter == utils.TruncateID(image.Get("Id")) {
|
||||
if matchName != "" {
|
||||
if matchName == image.Get("Id") || matchName == utils.TruncateID(image.Get("Id")) {
|
||||
startImage = image
|
||||
}
|
||||
|
||||
for _, repotag := range image.GetList("RepoTags") {
|
||||
if repotag == filter {
|
||||
if repotag == matchName {
|
||||
startImage = image
|
||||
}
|
||||
}
|
||||
|
@ -1211,16 +1246,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
root := engine.NewTable("Created", 1)
|
||||
root.Add(startImage)
|
||||
cli.WalkTree(*noTrunc, root, byParent, "", printNode)
|
||||
} else if filter == "" {
|
||||
} else if matchName == "" {
|
||||
cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
|
||||
}
|
||||
if *flViz {
|
||||
fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
|
||||
}
|
||||
} else {
|
||||
v := url.Values{}
|
||||
v := url.Values{
|
||||
"filters": []string{filters.ToParam(imageFilters)},
|
||||
}
|
||||
if cmd.NArg() == 1 {
|
||||
v.Set("filter", filter)
|
||||
// FIXME rename this parameter, to not be confused with the filters flag
|
||||
v.Set("filter", matchName)
|
||||
}
|
||||
if *all {
|
||||
v.Set("all", "1")
|
||||
|
|
|
@ -188,6 +188,8 @@ func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseW
|
|||
job = eng.Job("images")
|
||||
)
|
||||
|
||||
job.Setenv("filters", r.Form.Get("filters"))
|
||||
// FIXME rename this parameter, to not be confused with the filters flag
|
||||
job.Setenv("filter", r.Form.Get("filter"))
|
||||
job.Setenv("all", r.Form.Get("all"))
|
||||
|
||||
|
|
1499
docs/sources/reference/commandline/cli.rst
Normal file
1499
docs/sources/reference/commandline/cli.rst
Normal file
File diff suppressed because it is too large
Load diff
|
@ -55,6 +55,7 @@ import (
|
|||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/dotcloud/docker/utils/filters"
|
||||
)
|
||||
|
||||
func (srv *Server) handlerWrap(h engine.Handler) engine.Handler {
|
||||
|
@ -694,10 +695,23 @@ func (srv *Server) ImagesViz(job *engine.Job) engine.Status {
|
|||
|
||||
func (srv *Server) Images(job *engine.Job) engine.Status {
|
||||
var (
|
||||
allImages map[string]*image.Image
|
||||
err error
|
||||
allImages map[string]*image.Image
|
||||
err error
|
||||
filt_tagged = true
|
||||
)
|
||||
if job.GetenvBool("all") {
|
||||
|
||||
imageFilters, err := filters.ParseFlag(job.Getenv("filters"), nil)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
if i, ok := imageFilters["untagged"]; ok && strings.ToLower(i) == "true" {
|
||||
filt_tagged = false
|
||||
}
|
||||
if i, ok := imageFilters["tagged"]; ok && strings.ToLower(i) == "false" {
|
||||
filt_tagged = false
|
||||
}
|
||||
|
||||
if job.GetenvBool("all") && !filt_tagged {
|
||||
allImages, err = srv.daemon.Graph().Map()
|
||||
} else {
|
||||
allImages, err = srv.daemon.Graph().Heads()
|
||||
|
@ -721,17 +735,22 @@ func (srv *Server) Images(job *engine.Job) engine.Status {
|
|||
}
|
||||
|
||||
if out, exists := lookup[id]; exists {
|
||||
out.SetList("RepoTags", append(out.GetList("RepoTags"), fmt.Sprintf("%s:%s", name, tag)))
|
||||
if filt_tagged {
|
||||
out.SetList("RepoTags", append(out.GetList("RepoTags"), fmt.Sprintf("%s:%s", name, tag)))
|
||||
}
|
||||
} else {
|
||||
out := &engine.Env{}
|
||||
// get the boolean list for if only the untagged images are requested
|
||||
delete(allImages, id)
|
||||
out.Set("ParentId", image.Parent)
|
||||
out.SetList("RepoTags", []string{fmt.Sprintf("%s:%s", name, tag)})
|
||||
out.Set("Id", image.ID)
|
||||
out.SetInt64("Created", image.Created.Unix())
|
||||
out.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
||||
lookup[id] = out
|
||||
if filt_tagged {
|
||||
out := &engine.Env{}
|
||||
out.Set("ParentId", image.Parent)
|
||||
out.SetList("RepoTags", []string{fmt.Sprintf("%s:%s", name, tag)})
|
||||
out.Set("Id", image.ID)
|
||||
out.SetInt64("Created", image.Created.Unix())
|
||||
out.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
||||
lookup[id] = out
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
53
utils/filters/filter.go
Normal file
53
utils/filters/filter.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package filters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var DefaultFilterProcs = FilterProcSet{}
|
||||
|
||||
func Register(name string, fp FilterProc) error {
|
||||
return DefaultFilterProcs.Register(name, fp)
|
||||
}
|
||||
|
||||
var ErrorFilterExists = errors.New("filter already exists and ")
|
||||
var ErrorFilterExistsConflict = errors.New("filter already exists and FilterProc are different")
|
||||
|
||||
type FilterProcSet map[string]FilterProc
|
||||
|
||||
func (fs FilterProcSet) Process(context string) {
|
||||
}
|
||||
|
||||
func (fs FilterProcSet) Register(name string, fp FilterProc) error {
|
||||
if v, ok := fs[name]; ok {
|
||||
if v == fp {
|
||||
return ErrorFilterExists
|
||||
} else {
|
||||
return ErrorFilterExistsConflict
|
||||
}
|
||||
}
|
||||
fs[name] = fp
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilterProc interface {
|
||||
Process(context, key, value string, output io.Writer) error
|
||||
}
|
||||
|
||||
type UnknownFilterProc struct{}
|
||||
|
||||
func (ufp UnknownFilterProc) Process(context, key, value string, output io.Writer) error {
|
||||
if output != nil {
|
||||
fmt.Fprintf(output, "do not know how to process [%s : %s]", key, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Filter interface {
|
||||
Scope() string
|
||||
Target() string
|
||||
Expressions() []string
|
||||
Match(interface{}) bool
|
||||
}
|
47
utils/filters/parse.go
Normal file
47
utils/filters/parse.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package filters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
Parse the argument to the filter flag. Like
|
||||
|
||||
`docker ps -f 'created=today;image.name=ubuntu*'`
|
||||
|
||||
Filters delimited by ';', and expected to be 'name=value'
|
||||
|
||||
If prev map is provided, then it is appended to, and returned. By default a new
|
||||
map is created.
|
||||
*/
|
||||
func ParseFlag(arg string, prev map[string]string) (map[string]string, error) {
|
||||
var filters map[string]string
|
||||
if prev != nil {
|
||||
filters = prev
|
||||
} else {
|
||||
filters = map[string]string{}
|
||||
}
|
||||
if len(arg) == 0 {
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
for _, chunk := range strings.Split(arg, ";") {
|
||||
if !strings.Contains(chunk, "=") {
|
||||
return filters, ErrorBadFormat
|
||||
}
|
||||
f := strings.SplitN(chunk, "=", 2)
|
||||
filters[f[0]] = f[1]
|
||||
}
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
var ErrorBadFormat = errors.New("bad format of filter (expected name=value)")
|
||||
|
||||
func ToParam(f map[string]string) string {
|
||||
fs := []string{}
|
||||
for k, v := range f {
|
||||
fs = append(fs, k+"="+v)
|
||||
}
|
||||
return strings.Join(fs, ";")
|
||||
}
|
Loading…
Reference in a new issue