mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Set labels on container create
Signed-off-by: Darren Shepherd <darren@rancher.com>
This commit is contained in:
parent
cdfdfbfb62
commit
abb5e9a077
8 changed files with 264 additions and 11 deletions
|
@ -90,6 +90,10 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
|
|||
return nil
|
||||
}
|
||||
|
||||
if !psFilters.MatchKVList("label", container.Config.Labels) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if before != "" && !foundBefore {
|
||||
if container.ID == beforeCont.ID {
|
||||
foundBefore = true
|
||||
|
@ -157,6 +161,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
|
|||
out.SetInt64("SizeRw", sizeRw)
|
||||
out.SetInt64("SizeRootFs", sizeRootFs)
|
||||
}
|
||||
out.SetJson("Labels", container.Config.Labels)
|
||||
outs.Add(out)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -11,13 +11,17 @@ import (
|
|||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
)
|
||||
|
||||
var acceptedImageFilterTags = map[string]struct{}{"dangling": {}}
|
||||
var acceptedImageFilterTags = map[string]struct{}{
|
||||
"dangling": {},
|
||||
"label": {},
|
||||
}
|
||||
|
||||
func (s *TagStore) CmdImages(job *engine.Job) engine.Status {
|
||||
var (
|
||||
allImages map[string]*image.Image
|
||||
err error
|
||||
filt_tagged = true
|
||||
filt_label = false
|
||||
)
|
||||
|
||||
imageFilters, err := filters.FromParam(job.Getenv("filters"))
|
||||
|
@ -38,6 +42,8 @@ func (s *TagStore) CmdImages(job *engine.Job) engine.Status {
|
|||
}
|
||||
}
|
||||
|
||||
_, filt_label = imageFilters["label"]
|
||||
|
||||
if job.GetenvBool("all") && filt_tagged {
|
||||
allImages, err = s.graph.Map()
|
||||
} else {
|
||||
|
@ -68,6 +74,9 @@ func (s *TagStore) CmdImages(job *engine.Job) engine.Status {
|
|||
} else {
|
||||
// get the boolean list for if only the untagged images are requested
|
||||
delete(allImages, id)
|
||||
if !imageFilters.MatchKVList("label", image.ContainerConfig.Labels) {
|
||||
continue
|
||||
}
|
||||
if filt_tagged {
|
||||
out := &engine.Env{}
|
||||
out.SetJson("ParentId", image.Parent)
|
||||
|
@ -76,6 +85,7 @@ func (s *TagStore) CmdImages(job *engine.Job) engine.Status {
|
|||
out.SetInt64("Created", image.Created.Unix())
|
||||
out.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
||||
out.SetJson("Labels", image.ContainerConfig.Labels)
|
||||
lookup[id] = out
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +100,11 @@ func (s *TagStore) CmdImages(job *engine.Job) engine.Status {
|
|||
}
|
||||
|
||||
// Display images which aren't part of a repository/tag
|
||||
if job.Getenv("filter") == "" {
|
||||
if job.Getenv("filter") == "" || filt_label {
|
||||
for _, image := range allImages {
|
||||
if !imageFilters.MatchKVList("label", image.ContainerConfig.Labels) {
|
||||
continue
|
||||
}
|
||||
out := &engine.Env{}
|
||||
out.SetJson("ParentId", image.Parent)
|
||||
out.SetList("RepoTags", []string{"<none>:<none>"})
|
||||
|
@ -99,6 +112,7 @@ func (s *TagStore) CmdImages(job *engine.Job) engine.Status {
|
|||
out.SetInt64("Created", image.Created.Unix())
|
||||
out.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
||||
out.SetJson("Labels", image.ContainerConfig.Labels)
|
||||
outs.Add(out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -249,3 +250,57 @@ func TestCreateVolumesCreated(t *testing.T) {
|
|||
|
||||
logDone("create - volumes are created")
|
||||
}
|
||||
|
||||
func TestCreateLabels(t *testing.T) {
|
||||
name := "test_create_labels"
|
||||
expected := map[string]string{"k1": "v1", "k2": "v2"}
|
||||
if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "create", "--name", name, "-l", "k1=v1", "--label", "k2=v2", "busybox")); err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
|
||||
actual := make(map[string]string)
|
||||
err := inspectFieldAndMarshall(name, "Config.Labels", &actual)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("Expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("create - labels")
|
||||
}
|
||||
|
||||
func TestCreateLabelFromImage(t *testing.T) {
|
||||
imageName := "testcreatebuildlabel"
|
||||
defer deleteImages(imageName)
|
||||
_, err := buildImage(imageName,
|
||||
`FROM busybox
|
||||
LABEL k1=v1 k2=v2`,
|
||||
true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
name := "test_create_labels_from_image"
|
||||
expected := map[string]string{"k2": "x", "k3": "v3", "k1": "v1"}
|
||||
if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "create", "--name", name, "-l", "k2=x", "--label", "k3=v3", imageName)); err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
|
||||
actual := make(map[string]string)
|
||||
err = inspectFieldAndMarshall(name, "Config.Labels", &actual)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("Expected %s got %s", expected, actual)
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("create - labels from image")
|
||||
}
|
||||
|
|
|
@ -77,6 +77,60 @@ func TestImagesErrorWithInvalidFilterNameTest(t *testing.T) {
|
|||
logDone("images - invalid filter name check working")
|
||||
}
|
||||
|
||||
func TestImagesFilterLabel(t *testing.T) {
|
||||
imageName1 := "images_filter_test1"
|
||||
imageName2 := "images_filter_test2"
|
||||
imageName3 := "images_filter_test3"
|
||||
defer deleteAllContainers()
|
||||
defer deleteImages(imageName1)
|
||||
defer deleteImages(imageName2)
|
||||
defer deleteImages(imageName3)
|
||||
image1ID, err := buildImage(imageName1,
|
||||
`FROM scratch
|
||||
LABEL match me`, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
image2ID, err := buildImage(imageName2,
|
||||
`FROM scratch
|
||||
LABEL match="me too"`, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
image3ID, err := buildImage(imageName3,
|
||||
`FROM scratch
|
||||
LABEL nomatch me`, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(dockerBinary, "images", "--no-trunc", "-q", "-f", "label=match")
|
||||
out, _, err := runCommandWithOutput(cmd)
|
||||
if err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
out = strings.TrimSpace(out)
|
||||
|
||||
if (!strings.Contains(out, image1ID) && !strings.Contains(out, image2ID)) || strings.Contains(out, image3ID) {
|
||||
t.Fatalf("Expected ids %s,%s got %s", image1ID, image2ID, out)
|
||||
}
|
||||
|
||||
cmd = exec.Command(dockerBinary, "images", "--no-trunc", "-q", "-f", "label=match=me too")
|
||||
out, _, err = runCommandWithOutput(cmd)
|
||||
if err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
out = strings.TrimSpace(out)
|
||||
|
||||
if out != image2ID {
|
||||
t.Fatalf("Expected %s got %s", image2ID, out)
|
||||
}
|
||||
|
||||
logDone("images - filter label")
|
||||
}
|
||||
|
||||
func TestImagesFilterWhiteSpaceTrimmingAndLowerCasingWorking(t *testing.T) {
|
||||
imageName := "images_filter_test"
|
||||
defer deleteAllContainers()
|
||||
|
|
|
@ -412,6 +412,54 @@ func TestPsListContainersFilterName(t *testing.T) {
|
|||
logDone("ps - test ps filter name")
|
||||
}
|
||||
|
||||
func TestPsListContainersFilterLabel(t *testing.T) {
|
||||
// start container
|
||||
runCmd := exec.Command(dockerBinary, "run", "-d", "-l", "match=me", "busybox")
|
||||
out, _, err := runCommandWithOutput(runCmd)
|
||||
if err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
firstID := stripTrailingCharacters(out)
|
||||
|
||||
// start another container
|
||||
runCmd = exec.Command(dockerBinary, "run", "-d", "-l", "match=me too", "busybox")
|
||||
if out, _, err = runCommandWithOutput(runCmd); err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
secondID := stripTrailingCharacters(out)
|
||||
|
||||
// start third container
|
||||
runCmd = exec.Command(dockerBinary, "run", "-d", "-l", "nomatch=me", "busybox")
|
||||
if out, _, err = runCommandWithOutput(runCmd); err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
thirdID := stripTrailingCharacters(out)
|
||||
|
||||
// filter containers by exact match
|
||||
runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me")
|
||||
if out, _, err = runCommandWithOutput(runCmd); err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
containerOut := strings.TrimSpace(out)
|
||||
if containerOut != firstID {
|
||||
t.Fatalf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out)
|
||||
}
|
||||
|
||||
// filter containers by exact key
|
||||
runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--no-trunc", "--filter=label=match")
|
||||
if out, _, err = runCommandWithOutput(runCmd); err != nil {
|
||||
t.Fatal(out, err)
|
||||
}
|
||||
containerOut = strings.TrimSpace(out)
|
||||
if (!strings.Contains(containerOut, firstID) || !strings.Contains(containerOut, secondID)) || strings.Contains(containerOut, thirdID) {
|
||||
t.Fatalf("Expected ids %s,%s, got %s for exited filter, output: %q", firstID, secondID, containerOut, out)
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("ps - test ps filter label")
|
||||
}
|
||||
|
||||
func TestPsListContainersFilterExited(t *testing.T) {
|
||||
defer deleteAllContainers()
|
||||
|
||||
|
|
|
@ -724,6 +724,15 @@ COPY . /static`); err != nil {
|
|||
ctx: ctx}, nil
|
||||
}
|
||||
|
||||
func inspectFieldAndMarshall(name, field string, output interface{}) error {
|
||||
str, err := inspectFieldJSON(name, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(str), output)
|
||||
}
|
||||
|
||||
func inspectFilter(name, filter string) (string, error) {
|
||||
format := fmt.Sprintf("{{%s}}", filter)
|
||||
inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name)
|
||||
|
|
|
@ -65,6 +65,38 @@ func FromParam(p string) (Args, error) {
|
|||
return args, nil
|
||||
}
|
||||
|
||||
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||
fieldValues := filters[field]
|
||||
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if sources == nil || len(sources) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
outer:
|
||||
for _, name2match := range fieldValues {
|
||||
testKV := strings.SplitN(name2match, "=", 2)
|
||||
|
||||
for k, v := range sources {
|
||||
if len(testKV) == 1 {
|
||||
if k == testKV[0] {
|
||||
continue outer
|
||||
}
|
||||
} else if k == testKV[0] && v == testKV[1] {
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (filters Args) Match(field, source string) bool {
|
||||
fieldValues := filters[field]
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
flVolumes = opts.NewListOpts(opts.ValidatePath)
|
||||
flLinks = opts.NewListOpts(opts.ValidateLink)
|
||||
flEnv = opts.NewListOpts(opts.ValidateEnv)
|
||||
flLabels = opts.NewListOpts(opts.ValidateEnv)
|
||||
flDevices = opts.NewListOpts(opts.ValidatePath)
|
||||
|
||||
ulimits = make(map[string]*ulimit.Ulimit)
|
||||
|
@ -47,6 +48,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
flCapAdd = opts.NewListOpts(nil)
|
||||
flCapDrop = opts.NewListOpts(nil)
|
||||
flSecurityOpt = opts.NewListOpts(nil)
|
||||
flLabelsFile = opts.NewListOpts(nil)
|
||||
|
||||
flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
|
||||
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
|
||||
|
@ -74,6 +76,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
|
||||
cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container")
|
||||
cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container")
|
||||
cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container, for example com.example.key=value")
|
||||
cmd.Var(&flLabelsFile, []string{"-label-file"}, "Read in a line delimited file of labels")
|
||||
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
|
||||
cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a file of environment variables")
|
||||
cmd.Var(&flPublish, []string{"p", "-publish"}, "Publish a container's port(s) to the host")
|
||||
|
@ -243,16 +247,16 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
}
|
||||
|
||||
// collect all the environment variables for the container
|
||||
envVariables := []string{}
|
||||
for _, ef := range flEnvFile.GetAll() {
|
||||
parsedVars, err := opts.ParseEnvFile(ef)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
envVariables = append(envVariables, parsedVars...)
|
||||
envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
|
||||
// collect all the labels for the container
|
||||
labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
// parse the '-e' and '--env' after, to allow override
|
||||
envVariables = append(envVariables, flEnv.GetAll()...)
|
||||
|
||||
ipcMode := IpcMode(*flIpcMode)
|
||||
if !ipcMode.Valid() {
|
||||
|
@ -297,6 +301,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
MacAddress: *flMacAddress,
|
||||
Entrypoint: entrypoint,
|
||||
WorkingDir: *flWorkingDir,
|
||||
Labels: convertKVStringsToMap(labels),
|
||||
}
|
||||
|
||||
hostConfig := &HostConfig{
|
||||
|
@ -330,6 +335,37 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
return config, hostConfig, cmd, nil
|
||||
}
|
||||
|
||||
// reads a file of line terminated key=value pairs and override that with override parameter
|
||||
func readKVStrings(files []string, override []string) ([]string, error) {
|
||||
envVariables := []string{}
|
||||
for _, ef := range files {
|
||||
parsedVars, err := opts.ParseEnvFile(ef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envVariables = append(envVariables, parsedVars...)
|
||||
}
|
||||
// parse the '-e' and '--env' after, to allow override
|
||||
envVariables = append(envVariables, override...)
|
||||
|
||||
return envVariables, nil
|
||||
}
|
||||
|
||||
// converts ["key=value"] to {"key":"value"}
|
||||
func convertKVStringsToMap(values []string) map[string]string {
|
||||
result := make(map[string]string, len(values))
|
||||
for _, value := range values {
|
||||
kv := strings.SplitN(value, "=", 2)
|
||||
if len(kv) == 1 {
|
||||
result[kv[0]] = ""
|
||||
} else {
|
||||
result[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// parseRestartPolicy returns the parsed policy or an error indicating what is incorrect
|
||||
func parseRestartPolicy(policy string) (RestartPolicy, error) {
|
||||
p := RestartPolicy{}
|
||||
|
|
Loading…
Reference in a new issue