mirror of
https://github.com/docker/docs.git
synced 2026-11-11 04:02:07 +03:00
Merge pull request #1248 from dnephin/fix_label_ignored_by_ps
Support filtering images by labels
This commit is contained in:
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
@@ -104,7 +104,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/samalba/dockerclient",
|
||||
"Rev": "cfb489c624b635251a93e74e1e90eb0959c5367f"
|
||||
"Rev": "73edd1c3a9d280bcec6a22a12fc90c1a46558b4e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/samuel/go-zookeeper/zk",
|
||||
|
||||
2
Godeps/_workspace/src/github.com/samalba/dockerclient/types.go
generated
vendored
2
Godeps/_workspace/src/github.com/samalba/dockerclient/types.go
generated
vendored
@@ -272,7 +272,9 @@ type RespContainersCreate struct {
|
||||
type Image struct {
|
||||
Created int64
|
||||
Id string
|
||||
Labels map[string]string
|
||||
ParentId string
|
||||
RepoDigests []string
|
||||
RepoTags []string
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
|
||||
@@ -28,7 +28,7 @@ const APIVERSION = "1.16"
|
||||
func getInfo(c *context, w http.ResponseWriter, r *http.Request) {
|
||||
info := dockerclient.Info{
|
||||
Containers: int64(len(c.cluster.Containers())),
|
||||
Images: int64(len(c.cluster.Images(false))),
|
||||
Images: int64(len(c.cluster.Images(false, nil))),
|
||||
DriverStatus: c.statusHandler.Status(),
|
||||
NEventsListener: int64(c.eventsHandler.Size()),
|
||||
Debug: c.debug,
|
||||
@@ -78,7 +78,7 @@ func getImages(c *context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Create a map of engine address to the list of images it holds.
|
||||
engineImages := make(map[string][]*cluster.Image)
|
||||
for _, image := range c.cluster.Images(true) {
|
||||
for _, image := range c.cluster.Images(true, nil) {
|
||||
engineImages[image.Engine.Addr] = append(engineImages[image.Engine.Addr], image)
|
||||
}
|
||||
|
||||
@@ -122,11 +122,12 @@ func getImagesJSON(c *context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: apply node filter in engine?
|
||||
accepteds, _ := filters["node"]
|
||||
// this struct helps grouping images
|
||||
// but still keeps their Engine infos as an array.
|
||||
groupImages := make(map[string]dockerclient.Image)
|
||||
for _, image := range c.cluster.Images(all) {
|
||||
for _, image := range c.cluster.Images(all, filters) {
|
||||
if len(accepteds) != 0 {
|
||||
found := false
|
||||
for _, accepted := range accepteds {
|
||||
@@ -720,7 +721,7 @@ func proxyImage(c *context, w http.ResponseWriter, r *http.Request) {
|
||||
func proxyImageTagOptional(c *context, w http.ResponseWriter, r *http.Request) {
|
||||
name := mux.Vars(r)["name"]
|
||||
|
||||
for _, image := range c.cluster.Images(true) {
|
||||
for _, image := range c.cluster.Images(true, nil) {
|
||||
if len(strings.SplitN(name, ":", 2)) == 2 && image.Match(name, true) ||
|
||||
len(strings.SplitN(name, ":", 2)) == 1 && image.Match(name, false) {
|
||||
proxy(c.tlsConfig, image.Engine.Addr, w, r)
|
||||
|
||||
@@ -3,6 +3,7 @@ package cluster
|
||||
import (
|
||||
"io"
|
||||
|
||||
dockerfilters "github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,7 @@ type Cluster interface {
|
||||
RemoveContainer(container *Container, force, volumes bool) error
|
||||
|
||||
// Return all images
|
||||
Images(all bool) []*Image
|
||||
Images(all bool, filters dockerfilters.Args) []*Image
|
||||
|
||||
// Return one image matching `IDOrName`
|
||||
Image(IDOrName string) *Image
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
dockerfilters "github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/samalba/dockerclient"
|
||||
"github.com/samalba/dockerclient/nopclient"
|
||||
@@ -558,15 +559,28 @@ func (e *Engine) Containers() Containers {
|
||||
}
|
||||
|
||||
// Images returns all the images in the engine
|
||||
func (e *Engine) Images(all bool) []*Image {
|
||||
func (e *Engine) Images(all bool, filters dockerfilters.Args) []*Image {
|
||||
e.RLock()
|
||||
|
||||
includeAll := func(image *Image) bool {
|
||||
// TODO: this is wrong if RepoTags == []
|
||||
return all || (len(image.RepoTags) != 0 && image.RepoTags[0] != "<none>:<none>")
|
||||
}
|
||||
|
||||
includeFilter := func(image *Image) bool {
|
||||
if filters == nil {
|
||||
return true
|
||||
}
|
||||
return filters.MatchKVList("label", image.Labels)
|
||||
}
|
||||
|
||||
images := make([]*Image, 0, len(e.images))
|
||||
for _, image := range e.images {
|
||||
if all || (!all && len(image.RepoTags) != 0 && image.RepoTags[0] != "<none>:<none>") {
|
||||
if includeAll(image) && includeFilter(image) {
|
||||
images = append(images, image)
|
||||
}
|
||||
}
|
||||
// TODO: shouldn't this be defered immediately after locking?
|
||||
e.RUnlock()
|
||||
return images
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
dockerfilters "github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/samalba/dockerclient"
|
||||
"github.com/samalba/dockerclient/mockclient"
|
||||
"github.com/samalba/dockerclient/nopclient"
|
||||
@@ -217,6 +218,35 @@ func TestCreateContainer(t *testing.T) {
|
||||
assert.Len(t, engine.Containers(), 2)
|
||||
}
|
||||
|
||||
func TestImages(t *testing.T) {
|
||||
engine := NewEngine("test", 0)
|
||||
engine.images = []*Image{
|
||||
{dockerclient.Image{Id: "a"}, engine},
|
||||
{dockerclient.Image{Id: "b"}, engine},
|
||||
{dockerclient.Image{Id: "c"}, engine},
|
||||
}
|
||||
|
||||
result := engine.Images(true, nil)
|
||||
assert.Equal(t, len(result), 3)
|
||||
}
|
||||
|
||||
func TestImagesWithFilter(t *testing.T) {
|
||||
engine := NewEngine("test", 0)
|
||||
engine.images = []*Image{
|
||||
{dockerclient.Image{Id: "a"}, engine},
|
||||
{dockerclient.Image{
|
||||
Id: "b",
|
||||
Labels: map[string]string{"com.example.project": "bar"},
|
||||
}, engine},
|
||||
{dockerclient.Image{Id: "c"}, engine},
|
||||
}
|
||||
|
||||
filters := dockerfilters.Args{"label": {"com.example.project=bar"}}
|
||||
result := engine.Images(true, filters)
|
||||
assert.Equal(t, len(result), 1)
|
||||
assert.Equal(t, result[0].Id, "b")
|
||||
}
|
||||
|
||||
func TestTotalMemory(t *testing.T) {
|
||||
engine := NewEngine("test", 0.05)
|
||||
engine.Memory = 1024
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
dockerfilters "github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/cluster/mesos/queue"
|
||||
"github.com/docker/swarm/scheduler"
|
||||
@@ -190,13 +191,13 @@ func (c *Cluster) RemoveContainer(container *cluster.Container, force, volumes b
|
||||
}
|
||||
|
||||
// Images returns all the images in the cluster.
|
||||
func (c *Cluster) Images(all bool) []*cluster.Image {
|
||||
func (c *Cluster) Images(all bool, filters dockerfilters.Args) []*cluster.Image {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
out := []*cluster.Image{}
|
||||
for _, s := range c.slaves {
|
||||
out = append(out, s.engine.Images(all)...)
|
||||
out = append(out, s.engine.Images(all, filters)...)
|
||||
}
|
||||
|
||||
return out
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
dockerfilters "github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/units"
|
||||
"github.com/docker/swarm/cluster"
|
||||
@@ -237,13 +238,13 @@ func (c *Cluster) monitorDiscovery(ch <-chan discovery.Entries, errCh <-chan err
|
||||
}
|
||||
|
||||
// Images returns all the images in the cluster.
|
||||
func (c *Cluster) Images(all bool) []*cluster.Image {
|
||||
func (c *Cluster) Images(all bool, filters dockerfilters.Args) []*cluster.Image {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
out := []*cluster.Image{}
|
||||
for _, e := range c.engines {
|
||||
out = append(out, e.Images(all)...)
|
||||
out = append(out, e.Images(all, filters)...)
|
||||
}
|
||||
|
||||
return out
|
||||
@@ -276,7 +277,7 @@ func (c *Cluster) RemoveImages(name string, force bool) ([]*dockerclient.ImageDe
|
||||
errs := []string{}
|
||||
var err error
|
||||
for _, e := range c.engines {
|
||||
for _, image := range e.Images(true) {
|
||||
for _, image := range e.Images(true, nil) {
|
||||
if image.Match(name, true) {
|
||||
content, err := image.Engine.RemoveImage(image, name, force)
|
||||
if err != nil {
|
||||
@@ -698,7 +699,7 @@ func (c *Cluster) TagImage(IDOrName string, repo string, tag string, force bool)
|
||||
var err error
|
||||
found := false
|
||||
for _, e := range c.engines {
|
||||
for _, image := range e.Images(true) {
|
||||
for _, image := range e.Images(true, nil) {
|
||||
if image.Match(IDOrName, true) {
|
||||
found = true
|
||||
err := image.Engine.TagImage(IDOrName, repo, tag, force)
|
||||
|
||||
@@ -34,7 +34,7 @@ func NewNode(e *cluster.Engine) *Node {
|
||||
Name: e.Name,
|
||||
Labels: e.Labels,
|
||||
Containers: e.Containers(),
|
||||
Images: e.Images(true),
|
||||
Images: e.Images(true, nil),
|
||||
UsedMemory: e.UsedMemory(),
|
||||
UsedCpus: e.UsedCpus(),
|
||||
TotalMemory: e.TotalMemory(),
|
||||
|
||||
@@ -43,3 +43,16 @@ function teardown() {
|
||||
run docker_swarm images -a
|
||||
[ "${#lines[@]}" -ge 3 ]
|
||||
}
|
||||
|
||||
@test "docker images -f label" {
|
||||
start_docker_with_busybox 2
|
||||
swarm_manage
|
||||
|
||||
docker_swarm build -t image-with-labels $TESTDATA/imagelabel
|
||||
|
||||
run docker_swarm images \
|
||||
--filter label=com.docker.swarm.test.integration.images=labeltest
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 2 ]
|
||||
[[ "${lines[1]}" == *"image-with-labels"* ]]
|
||||
}
|
||||
|
||||
2
test/integration/testdata/imagelabel/Dockerfile
vendored
Normal file
2
test/integration/testdata/imagelabel/Dockerfile
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
FROM scratch
|
||||
LABEL com.docker.swarm.test.integration.images=labeltest
|
||||
Reference in New Issue
Block a user