From cdfdfbfb6223fdd5b319942d412caac6bc09cdeb Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Tue, 17 Feb 2015 07:20:06 -0800 Subject: [PATCH 1/7] Allow specification of Label Name/Value pairs in image json content Save "LABEL" field in Dockerfile into image content. This will allow a user to save user data into an image, which can later be retrieved using: docker inspect IMAGEID I have copied this from the "Comment" handling in docker images. We want to be able to add Name/Value data to an image to describe the image, and then be able to use other tools to look at this data, to be able to do security checks based on this data. We are thinking about adding version names, Perhaps listing the content of the dockerfile. Descriptions of where the code came from etc. This LABEL field should also be allowed to be specified in the docker import --change LABEL:Name=Value docker commit --change LABEL:Name=Value Docker-DCO-1.1-Signed-off-by: Dan Walsh (github: rhatdan) --- builder/command/command.go | 1 + builder/dispatchers.go | 31 +++++++++++++++++++ builder/evaluator.go | 1 + builder/parser/line_parsers.go | 20 ++++++++---- builder/parser/parser.go | 1 + contrib/syntax/kate/Dockerfile.xml | 1 + .../Syntaxes/Dockerfile.tmLanguage | 2 +- contrib/syntax/vim/syntax/dockerfile.vim | 2 +- .../reference/api/docker_remote_api.md | 7 +++++ .../reference/api/docker_remote_api_v1.17.md | 6 ++++ docs/sources/reference/builder.md | 12 +++++++ integration-cli/docker_cli_build_test.go | 22 +++++++++++++ runconfig/config.go | 5 +++ runconfig/merge.go | 10 ++++++ 14 files changed, 113 insertions(+), 8 deletions(-) diff --git a/builder/command/command.go b/builder/command/command.go index f99fa2d906..dd24ee44c5 100644 --- a/builder/command/command.go +++ b/builder/command/command.go @@ -3,6 +3,7 @@ package command const ( Env = "env" + Label = "label" Maintainer = "maintainer" Add = "add" Copy = "copy" diff --git a/builder/dispatchers.go b/builder/dispatchers.go index 52757281f3..965fd68c03 100644 --- a/builder/dispatchers.go +++ b/builder/dispatchers.go @@ -85,6 +85,37 @@ func maintainer(b *Builder, args []string, attributes map[string]bool, original return b.commit("", b.Config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer)) } +// LABEL some json data describing the image +// +// Sets the Label variable foo to bar, +// +func label(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) == 0 { + return fmt.Errorf("LABEL is missing arguments") + } + if len(args)%2 != 0 { + // should never get here, but just in case + return fmt.Errorf("Bad input to LABEL, too many args") + } + + commitStr := "LABEL" + + if b.Config.Labels == nil { + b.Config.Labels = map[string]string{} + } + + for j := 0; j < len(args); j++ { + // name ==> args[j] + // value ==> args[j+1] + newVar := args[j] + "=" + args[j+1] + "" + commitStr += " " + newVar + + b.Config.Labels[args[j]] = args[j+1] + j++ + } + return b.commit("", b.Config.Cmd, commitStr) +} + // ADD foo /path // // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling diff --git a/builder/evaluator.go b/builder/evaluator.go index eadef4a1e0..389fcc25e8 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -62,6 +62,7 @@ var evaluateTable map[string]func(*Builder, []string, map[string]bool, string) e func init() { evaluateTable = map[string]func(*Builder, []string, map[string]bool, string) error{ command.Env: env, + command.Label: label, command.Maintainer: maintainer, command.Add: add, command.Copy: dispatchCopy, // copy() is a go builtin diff --git a/builder/parser/line_parsers.go b/builder/parser/line_parsers.go index c7fed13dbe..06115e831a 100644 --- a/builder/parser/line_parsers.go +++ b/builder/parser/line_parsers.go @@ -44,10 +44,10 @@ func parseSubCommand(rest string) (*Node, map[string]bool, error) { // parse environment like statements. Note that this does *not* handle // variable interpolation, which will be handled in the evaluator. -func parseEnv(rest string) (*Node, map[string]bool, error) { +func parseNameVal(rest string, key string) (*Node, map[string]bool, error) { // This is kind of tricky because we need to support the old - // variant: ENV name value - // as well as the new one: ENV name=value ... + // variant: KEY name value + // as well as the new one: KEY name=value ... // The trigger to know which one is being used will be whether we hit // a space or = first. space ==> old, "=" ==> new @@ -137,10 +137,10 @@ func parseEnv(rest string) (*Node, map[string]bool, error) { } if len(words) == 0 { - return nil, nil, fmt.Errorf("ENV requires at least one argument") + return nil, nil, fmt.Errorf(key + " requires at least one argument") } - // Old format (ENV name value) + // Old format (KEY name value) var rootnode *Node if !strings.Contains(words[0], "=") { @@ -149,7 +149,7 @@ func parseEnv(rest string) (*Node, map[string]bool, error) { strs := TOKEN_WHITESPACE.Split(rest, 2) if len(strs) < 2 { - return nil, nil, fmt.Errorf("ENV must have two arguments") + return nil, nil, fmt.Errorf(key + " must have two arguments") } node.Value = strs[0] @@ -182,6 +182,14 @@ func parseEnv(rest string) (*Node, map[string]bool, error) { return rootnode, nil, nil } +func parseEnv(rest string) (*Node, map[string]bool, error) { + return parseNameVal(rest, "ENV") +} + +func parseLabel(rest string) (*Node, map[string]bool, error) { + return parseNameVal(rest, "LABEL") +} + // parses a whitespace-delimited set of arguments. The result is effectively a // linked list of string arguments. func parseStringsWhitespaceDelimited(rest string) (*Node, map[string]bool, error) { diff --git a/builder/parser/parser.go b/builder/parser/parser.go index 69bbfd0dc1..1ab151b30d 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -50,6 +50,7 @@ func init() { command.Onbuild: parseSubCommand, command.Workdir: parseString, command.Env: parseEnv, + command.Label: parseLabel, command.Maintainer: parseString, command.From: parseString, command.Add: parseMaybeJSONToList, diff --git a/contrib/syntax/kate/Dockerfile.xml b/contrib/syntax/kate/Dockerfile.xml index e5602397ba..4fdef2393b 100644 --- a/contrib/syntax/kate/Dockerfile.xml +++ b/contrib/syntax/kate/Dockerfile.xml @@ -22,6 +22,7 @@ CMD WORKDIR USER + LABEL diff --git a/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage b/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage index 1d19a3ba2e..75efc2e811 100644 --- a/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage +++ b/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage @@ -12,7 +12,7 @@ match - ^\s*(ONBUILD\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|WORKDIR|COPY)\s + ^\s*(ONBUILD\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|LABEL|WORKDIR|COPY)\s captures 0 diff --git a/contrib/syntax/vim/syntax/dockerfile.vim b/contrib/syntax/vim/syntax/dockerfile.vim index 2984bec5f8..36691e2504 100644 --- a/contrib/syntax/vim/syntax/dockerfile.vim +++ b/contrib/syntax/vim/syntax/dockerfile.vim @@ -11,7 +11,7 @@ let b:current_syntax = "dockerfile" syntax case ignore -syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|CMD|ENTRYPOINT|ENV|EXPOSE|FROM|MAINTAINER|RUN|USER|VOLUME|WORKDIR|COPY)\s/ +syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|CMD|ENTRYPOINT|ENV|EXPOSE|FROM|MAINTAINER|RUN|USER|LABEL|VOLUME|WORKDIR|COPY)\s/ highlight link dockerfileKeyword Keyword syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/ diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index 051b90ce97..0919d511b7 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -71,6 +71,13 @@ This endpoint now returns `SystemTime`, `HttpProxy`,`HttpsProxy` and `NoProxy`. ### What's new +**New!** +Build now has support for `LABEL` command which can be used to add user data +to an image. For example you could add data describing the content of an image. + +`LABEL "Vendor"="ACME Incorporated"` + +**New!** `POST /containers/(id)/attach` and `POST /exec/(id)/start` **New!** diff --git a/docs/sources/reference/api/docker_remote_api_v1.17.md b/docs/sources/reference/api/docker_remote_api_v1.17.md index 96887559c2..f4e16b29dc 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.17.md +++ b/docs/sources/reference/api/docker_remote_api_v1.17.md @@ -129,6 +129,11 @@ Create a container ], "Entrypoint": "", "Image": "ubuntu", + "Labels": { + "Vendor": "Acme", + "License": "GPL", + "Version": "1.0" + }, "Volumes": { "/tmp": {} }, @@ -1169,6 +1174,7 @@ Return low-level information on the image `name` "Cmd": ["/bin/bash"], "Dns": null, "Image": "ubuntu", + "Labels": null, "Volumes": null, "VolumesFrom": "", "WorkingDir": "" diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index 3d34db38cc..00c70e6fa1 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -328,6 +328,17 @@ default specified in `CMD`. > the result; `CMD` does not execute anything at build time, but specifies > the intended command for the image. +## LABEL + LABEL = = = ... + + --The `LABEL` instruction allows you to describe the image your `Dockerfile` +is building. `LABEL` is specified as name value pairs. This data can +be retrieved using the `docker inspect` command + + + LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" + LABEL Version="1.0" + ## EXPOSE EXPOSE [...] @@ -907,6 +918,7 @@ For example you might add something like this: FROM ubuntu MAINTAINER Victor Vieux + LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0" RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server # Firefox over VNC diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index ecef76f9da..c7749be233 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -4541,6 +4541,28 @@ func TestBuildWithTabs(t *testing.T) { logDone("build - with tabs") } +func TestBuildLabels(t *testing.T) { + name := "testbuildlabel" + expected := `{"License":"GPL","Vendor":"Acme"}` + defer deleteImages(name) + _, err := buildImage(name, + `FROM busybox + LABEL Vendor=Acme + LABEL License GPL`, + true) + if err != nil { + t.Fatal(err) + } + res, err := inspectFieldJSON(name, "Config.Labels") + if err != nil { + t.Fatal(err) + } + if res != expected { + t.Fatalf("Labels %s, expected %s", res, expected) + } + logDone("build - label") +} + func TestBuildStderr(t *testing.T) { // This test just makes sure that no non-error output goes // to stderr diff --git a/runconfig/config.go b/runconfig/config.go index ca5c3240b6..bcd3c46ec5 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -33,6 +33,8 @@ type Config struct { NetworkDisabled bool MacAddress string OnBuild []string + SecurityOpt []string + Labels map[string]string } func ContainerConfigFromJob(job *engine.Job) *Config { @@ -66,6 +68,9 @@ func ContainerConfigFromJob(job *engine.Job) *Config { if Cmd := job.GetenvList("Cmd"); Cmd != nil { config.Cmd = Cmd } + + job.GetenvJson("Labels", &config.Labels) + if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil { config.Entrypoint = Entrypoint } diff --git a/runconfig/merge.go b/runconfig/merge.go index 9bc4748446..9bbdc6ad25 100644 --- a/runconfig/merge.go +++ b/runconfig/merge.go @@ -84,6 +84,16 @@ func Merge(userConf, imageConf *Config) error { } } + if userConf.Labels == nil { + userConf.Labels = map[string]string{} + } + if imageConf.Labels != nil { + for l := range userConf.Labels { + imageConf.Labels[l] = userConf.Labels[l] + } + userConf.Labels = imageConf.Labels + } + if len(userConf.Entrypoint) == 0 { if len(userConf.Cmd) == 0 { userConf.Cmd = imageConf.Cmd From abb5e9a0777469e64fe2c7ecfa66ea01083d2071 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Tue, 6 Jan 2015 17:04:10 -0700 Subject: [PATCH 2/7] Set labels on container create Signed-off-by: Darren Shepherd --- daemon/list.go | 5 +++ graph/list.go | 18 +++++++- integration-cli/docker_cli_create_test.go | 55 +++++++++++++++++++++++ integration-cli/docker_cli_images_test.go | 54 ++++++++++++++++++++++ integration-cli/docker_cli_ps_test.go | 48 ++++++++++++++++++++ integration-cli/docker_utils.go | 9 ++++ pkg/parsers/filters/parse.go | 32 +++++++++++++ runconfig/parse.go | 54 ++++++++++++++++++---- 8 files changed, 264 insertions(+), 11 deletions(-) diff --git a/daemon/list.go b/daemon/list.go index 5885ebd499..174ec7ec75 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -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 } diff --git a/graph/list.go b/graph/list.go index 49d4072be5..9551edaae2 100644 --- a/graph/list.go +++ b/graph/list.go @@ -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{":"}) @@ -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) } } diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go index fe402caa4a..e32400e603 100644 --- a/integration-cli/docker_cli_create_test.go +++ b/integration-cli/docker_cli_create_test.go @@ -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") +} diff --git a/integration-cli/docker_cli_images_test.go b/integration-cli/docker_cli_images_test.go index bb24e9a347..2b22aa25e3 100644 --- a/integration-cli/docker_cli_images_test.go +++ b/integration-cli/docker_cli_images_test.go @@ -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() diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index d5f9d00dcb..d6d8d2ab5c 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -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() diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 9466d13256..1bc9fb5af5 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -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) diff --git a/pkg/parsers/filters/parse.go b/pkg/parsers/filters/parse.go index 8b045a3098..9c056bb3cf 100644 --- a/pkg/parsers/filters/parse.go +++ b/pkg/parsers/filters/parse.go @@ -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] diff --git a/runconfig/parse.go b/runconfig/parse.go index bf87c42f94..cc638efc1b 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -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{} From 389eee1084ea7613fa56e5f6b3e24678bf9aebc2 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Thu, 22 Jan 2015 13:47:10 -0700 Subject: [PATCH 3/7] Add docs for container/image labels Signed-off-by: Darren Shepherd --- docs/sources/reference/api/docker_remote_api.md | 17 +++++++++++++++++ .../reference/api/docker_remote_api_v1.17.md | 12 +++++++++++- docs/sources/reference/commandline/cli.md | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index 0919d511b7..95a3535d0c 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -83,10 +83,21 @@ to an image. For example you could add data describing the content of an image. **New!** Docker client now hints potential proxies about connection hijacking using HTTP Upgrade headers. +`POST /containers/create` + +**New!** +You can set labels on container create describing the container. + +`GET /containers/json` + +**New!** +This endpoint now returns the labels associated with each container (`Labels`). + `GET /containers/(id)/json` **New!** This endpoint now returns the list current execs associated with the container (`ExecIDs`). +This endpoint now returns the container labels (`Config.Labels`). `POST /containers/(id)/rename` @@ -105,6 +116,12 @@ root filesystem as read only. **New!** This endpoint returns a live stream of a container's resource usage statistics. +`GET /images/json` + +**New!** +This endpoint now returns the labels associated with each image (`Labels`). + + ## v1.16 ### Full Documentation diff --git a/docs/sources/reference/api/docker_remote_api_v1.17.md b/docs/sources/reference/api/docker_remote_api_v1.17.md index f4e16b29dc..050e027465 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.17.md +++ b/docs/sources/reference/api/docker_remote_api_v1.17.md @@ -194,6 +194,7 @@ Json Parameters: - **OpenStdin** - Boolean value, opens stdin, - **StdinOnce** - Boolean value, close stdin after the 1 attached client disconnects. - **Env** - A list of environment variables in the form of `VAR=value` +- **Labels** - A list of labels that will applied in the form of `VAR=value` - **Cmd** - Command to run specified as a string or an array of strings. - **Entrypoint** - Set the entrypoint for the container a a string or an array of strings @@ -303,6 +304,11 @@ Return low-level information on the container `id` "ExposedPorts": null, "Hostname": "ba033ac44011", "Image": "ubuntu", + "Labels": { + "Vendor": "Acme", + "License": "GPL", + "Version": "1.0" + }, "MacAddress": "", "Memory": 0, "MemorySwap": 0, @@ -1174,7 +1180,11 @@ Return low-level information on the image `name` "Cmd": ["/bin/bash"], "Dns": null, "Image": "ubuntu", - "Labels": null, + "Labels": { + "Vendor": "Acme", + "License": "GPL", + "Version": "1.0" + }, "Volumes": null, "VolumesFrom": "", "WorkingDir": "" diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 1f61432b5a..08eb606c7b 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -1662,6 +1662,8 @@ removed before the image is removed. --link=[] Add link to another container --lxc-conf=[] Add custom lxc options -m, --memory="" Memory limit + -l, --label=[] Set meta data on a container, for example com.example.key=value + -label-file=[] Read in a line delimited file of labels --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) --memory-swap="" Total memory (memory + swap), '-1' to disable swap --name="" Assign a name to the container From 7d89e66dac59999ae2f07970b273e227fdf73ea7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 17 Feb 2015 01:36:03 +0100 Subject: [PATCH 4/7] Add labels documentation Adds more documentation for labels and adds the label instruction to the man-pages. Also included is a document called "Labels - custom meta-data in Docker" in the user-guide, this is still a work-in-progress I started to describe the "namespaces" conventions, an example on storing structured data. I ran a bit "out of steam" (writers block?) on that document, but kept it in (for now), in case it still ends up useful. The Remote API documentation changes will need to be moved to the docker_remote_api_v1.18.md document when rebasing the whole PR. Signed-off-by: Sebastiaan van Stijn Signed-off-by: Darren Shepherd --- docs/man/Dockerfile.5.md | 12 ++ docs/man/docker-create.1.md | 8 + docs/man/docker-images.1.md | 4 +- docs/man/docker-inspect.1.md | 5 + docs/man/docker-ps.1.md | 1 + docs/man/docker-run.1.md | 8 + docs/mkdocs.yml | 1 + .../reference/api/docker_remote_api.md | 2 +- .../reference/api/docker_remote_api_v1.17.md | 25 +-- docs/sources/reference/builder.md | 18 +- docs/sources/reference/commandline/cli.md | 37 +++- .../userguide/labels-custom-metadata.md | 194 ++++++++++++++++++ runconfig/parse.go | 2 +- 13 files changed, 296 insertions(+), 21 deletions(-) create mode 100644 docs/sources/userguide/labels-custom-metadata.md diff --git a/docs/man/Dockerfile.5.md b/docs/man/Dockerfile.5.md index d66bcad067..48e39ac3c3 100644 --- a/docs/man/Dockerfile.5.md +++ b/docs/man/Dockerfile.5.md @@ -143,6 +143,18 @@ A Dockerfile is similar to a Makefile. **CMD** executes nothing at build time, but specifies the intended command for the image. +**LABEL** + --**LABEL [=] [[=] ...]** + + The **LABEL** instruction allows you to add meta-data to the image your + Dockerfile is building. LABEL is specified as name value pairs. This data can + be retrieved using the `docker inspect` command. + + The LABEL instruction allows for multiple labels to be set at one time. Like + command line parsing, quotes and backslashes can be used to include spaces + within values. + + **EXPOSE** -- `EXPOSE [...]` The **EXPOSE** instruction informs Docker that the container listens on the diff --git a/docs/man/docker-create.1.md b/docs/man/docker-create.1.md index d5166ca93a..7fc7f98152 100644 --- a/docs/man/docker-create.1.md +++ b/docs/man/docker-create.1.md @@ -24,6 +24,8 @@ docker-create - Create a new container [**--help**] [**-i**|**--interactive**[=*false*]] [**--ipc**[=*IPC*]] +[**-l**|**--label**[=*[]*]] +[**--label-file**[=*[]*]] [**--link**[=*[]*]] [**--lxc-conf**[=*[]*]] [**-m**|**--memory**[=*MEMORY*]] @@ -102,6 +104,12 @@ IMAGE [COMMAND] [ARG...] 'container:': reuses another container shared memory, semaphores and message queues 'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. +**-l**, **--label**=[] + Set meta data on the container (e.g., --label=com.example.key=value) + +**--label-file**=[] + Read in a line delimited file of labels + **--link**=[] Add link to another container in the form of :alias diff --git a/docs/man/docker-images.1.md b/docs/man/docker-images.1.md index 16fad991ce..253bb07ce2 100644 --- a/docs/man/docker-images.1.md +++ b/docs/man/docker-images.1.md @@ -34,7 +34,9 @@ versions. Show all images (by default filter out the intermediate image layers). The default is *false*. **-f**, **--filter**=[] - Provide filter values (i.e., 'dangling=true') + Provide filter values. Valid filters: + dangling=true - unlabeled images with no children + label= or label== **--help** Print usage statement diff --git a/docs/man/docker-inspect.1.md b/docs/man/docker-inspect.1.md index 23ec6bedef..85f6730004 100644 --- a/docs/man/docker-inspect.1.md +++ b/docs/man/docker-inspect.1.md @@ -83,6 +83,11 @@ To get information on a container use it's ID or instance name: "Ghost": false }, "Image": "df53773a4390e25936f9fd3739e0c0e60a62d024ea7b669282b27e65ae8458e6", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, "NetworkSettings": { "IPAddress": "172.17.0.2", "IPPrefixLen": 16, diff --git a/docs/man/docker-ps.1.md b/docs/man/docker-ps.1.md index bd1e04d813..783f4621f6 100644 --- a/docs/man/docker-ps.1.md +++ b/docs/man/docker-ps.1.md @@ -36,6 +36,7 @@ the running containers. **-f**, **--filter**=[] Provide filter values. Valid filters: exited= - containers with exit code of + label= or label== status=(restarting|running|paused|exited) name= - container's name id= - container's ID diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 587b9a2cd5..4b405920c2 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -25,6 +25,8 @@ docker-run - Run a command in a new container [**--help**] [**-i**|**--interactive**[=*false*]] [**--ipc**[=*IPC*]] +[**-l**|**--label**[=*[]*]] +[**--label-file**[=*[]*]] [**--link**[=*[]*]] [**--lxc-conf**[=*[]*]] [**-m**|**--memory**[=*MEMORY*]] @@ -197,6 +199,12 @@ ENTRYPOINT. 'container:': reuses another container shared memory, semaphores and message queues 'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. +**-l**, **--label**=[] + Set meta data on the container (e.g., --label=com.example.key=value) + +**--label-file**=[] + Read in a line delimited file of labels + **--link**=[] Add link to another container in the form of :alias diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7e514d542f..51dd388677 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -59,6 +59,7 @@ pages: - ['userguide/dockerimages.md', 'User Guide', 'Working with Docker Images' ] - ['userguide/dockerlinks.md', 'User Guide', 'Linking containers together' ] - ['userguide/dockervolumes.md', 'User Guide', 'Managing data in containers' ] +- ['userguide/labels-custom-metadata.md', 'User Guide', 'Labels - custom meta-data in Docker' ] - ['userguide/dockerrepos.md', 'User Guide', 'Working with Docker Hub' ] - ['userguide/level1.md', '**HIDDEN**' ] - ['userguide/level2.md', '**HIDDEN**' ] diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index 95a3535d0c..5e154b67e4 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -75,7 +75,7 @@ This endpoint now returns `SystemTime`, `HttpProxy`,`HttpsProxy` and `NoProxy`. Build now has support for `LABEL` command which can be used to add user data to an image. For example you could add data describing the content of an image. -`LABEL "Vendor"="ACME Incorporated"` +`LABEL "com.example.vendor"="ACME Incorporated"` **New!** `POST /containers/(id)/attach` and `POST /exec/(id)/start` diff --git a/docs/sources/reference/api/docker_remote_api_v1.17.md b/docs/sources/reference/api/docker_remote_api_v1.17.md index 050e027465..d82faffe98 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.17.md +++ b/docs/sources/reference/api/docker_remote_api_v1.17.md @@ -130,9 +130,9 @@ Create a container "Entrypoint": "", "Image": "ubuntu", "Labels": { - "Vendor": "Acme", - "License": "GPL", - "Version": "1.0" + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" }, "Volumes": { "/tmp": {} @@ -194,7 +194,8 @@ Json Parameters: - **OpenStdin** - Boolean value, opens stdin, - **StdinOnce** - Boolean value, close stdin after the 1 attached client disconnects. - **Env** - A list of environment variables in the form of `VAR=value` -- **Labels** - A list of labels that will applied in the form of `VAR=value` +- **Labels** - A map of labels and their values that will be added to the + container. It should be specified in the form `{"name":"value"[,"name2":"value2"]}` - **Cmd** - Command to run specified as a string or an array of strings. - **Entrypoint** - Set the entrypoint for the container a a string or an array of strings @@ -304,11 +305,11 @@ Return low-level information on the container `id` "ExposedPorts": null, "Hostname": "ba033ac44011", "Image": "ubuntu", - "Labels": { - "Vendor": "Acme", - "License": "GPL", - "Version": "1.0" - }, + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, "MacAddress": "", "Memory": 0, "MemorySwap": 0, @@ -1181,9 +1182,9 @@ Return low-level information on the image `name` "Dns": null, "Image": "ubuntu", "Labels": { - "Vendor": "Acme", - "License": "GPL", - "Version": "1.0" + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" }, "Volumes": null, "VolumesFrom": "", diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index 00c70e6fa1..ff5821286d 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -331,13 +331,23 @@ default specified in `CMD`. ## LABEL LABEL = = = ... - --The `LABEL` instruction allows you to describe the image your `Dockerfile` -is building. `LABEL` is specified as name value pairs. This data can +The `LABEL` instruction allows you to add meta-data to the image your +`Dockerfile` is building. `LABEL` is specified as name value pairs. This data can be retrieved using the `docker inspect` command + LABEL com.example.label-without-value + LABEL com.example.label-with-value="foo" + LABEL version="1.0" + LABEL description="This my ACME image" vendor="ACME Products" - LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" - LABEL Version="1.0" +As illustrated above, the `LABEL` instruction allows for multiple labels to be +set at one time. Like command line parsing, quotes and backslashes can be used +to include spaces within values. + +For example: + + LABEL description="This text illustrates \ + that label-values can span multiple lines." ## EXPOSE diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 08eb606c7b..79dbb6c8f5 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -791,6 +791,8 @@ Creates a new container. -h, --hostname="" Container host name -i, --interactive=false Keep STDIN open even if not attached --ipc="" IPC namespace to use + -l, --label=[] Set meta data on the container (e.g., --label=com.example.key=value) + --label-file=[] Read in a line delimited file of labels --link=[] Add link to another container --lxc-conf=[] Add custom lxc options -m, --memory="" Memory limit @@ -1143,6 +1145,7 @@ than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "b Current filters: * dangling (boolean - true or false) + * label (`label=` or `label==`) ##### Untagged images @@ -1662,8 +1665,8 @@ removed before the image is removed. --link=[] Add link to another container --lxc-conf=[] Add custom lxc options -m, --memory="" Memory limit - -l, --label=[] Set meta data on a container, for example com.example.key=value - -label-file=[] Read in a line delimited file of labels + -l, --label=[] Set meta data on the container (e.g., --label=com.example.key=value) + --label-file=[] Read in a line delimited file of labels --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) --memory-swap="" Total memory (memory + swap), '-1' to disable swap --name="" Assign a name to the container @@ -1837,6 +1840,36 @@ An example of a file passed with `--env-file` This will create and run a new container with the container name being `console`. + $ sudo docker run -l my-label --env com.example.foo=bar ubuntu bash + +This sets two labels on the container. Label "my-label" doesn't have a value +specified and will default to "" (empty string) for its value. Both `-l` and +`--env` can be repeated to add more labels. Label names are unique; if the same +label is specified multiple times, latter values overwrite the previous value. + +Labels can also be loaded from a line delimited file of labels using the +`--label-file` flag. The example below will load labels from a file named `labels` +in the current directory; + + $ sudo docker run --env-file ./labels ubuntu bash + +The format of the labels-file is similar to that used for loading environment +variables (see `--env-file` above). An example of a file passed with `--env-file`; + + $ cat ./labels + com.example.label1="a label" + + # this is a comment + com.example.label2=another\ label + com.example.label3 + +Multiple label-files can be loaded by providing the `--label-file` multiple +times. + +For additional information on working with labels, see +[*Labels - custom meta-data in Docker*](/userguide/labels-custom-metadata/) in +the user guide. + $ sudo docker run --link /redis:redis --name console ubuntu bash The `--link` flag will link the container named `/redis` into the newly diff --git a/docs/sources/userguide/labels-custom-metadata.md b/docs/sources/userguide/labels-custom-metadata.md new file mode 100644 index 0000000000..0824af4d62 --- /dev/null +++ b/docs/sources/userguide/labels-custom-metadata.md @@ -0,0 +1,194 @@ +page_title: Labels - custom meta-data in Docker +page_description: Learn how to work with custom meta-data in Docker, using labels. +page_keywords: Usage, user guide, labels, meta-data, docker, documentation, examples, annotating + +## Labels - custom meta-data in Docker + +Docker enables you to add meta-data to your images, containers, and daemons via +labels. Meta-data can serve a wide range of uses ranging from adding notes or +licensing information to an image to identifying a host. + +Labels in Docker are implemented as `` / `` pairs and values are +stored as *strings*. + +>**note:** Support for daemon-labels was added in docker 1.4.1, labels on +>containers and images in docker 1.6.0 + +### Naming your labels - namespaces + +Docker puts no hard restrictions on the names you pick for your labels, however, +it's easy for labels to "conflict". + +For example, you're building a tool that categorizes your images in +architectures, doing so by using an "architecture" label; + + LABEL architecture="amd64" + + LABEL architecture="ARMv7" + +But a user decided to label images by Architectural style + + LABEL architecture="Art Nouveau" + +To prevent such conflicts, Docker uses the convention to namespace label-names, +using a reverse domain notation; + + +- All (third-party) tools should namespace (prefix) their labels with the + reverse DNS notation of a domain controlled by the author of the tool. For + example, "com.example.some-label". +- Namespaced labels should only consist of lower-cased alphanumeric characters, + dots and dashes (in short, `[a-z0-9-.]`), should start *and* end with an alpha + numeric character, and may not contain consecutive dots or dashes. +- The `com.docker.*`, `io.docker.*` and `com.dockerproject.*` namespaces are + reserved for Docker's internal use. +- Labels *without* namespace (dots) are reserved for CLI use. This allows end- + users to add meta-data to their containers and images, without having to type + cumbersome namespaces on the command-line. + + +> **Note:** Even though Docker does not *enforce* you to use namespaces, +> preventing to do so will likely result in conflicting usage of labels. +> If you're building a tool that uses labels, you *should* use namespaces +> for your labels. + + +### Storing structured data in labels + +Labels can store any type of data, as long as its stored as a string. + + + { + "Description": "A containerized foobar", + "Usage": "docker run --rm example/foobar [args]", + "License": "GPL", + "Version": "0.0.1-beta", + "aBoolean": true, + "aNumber" : 0.01234, + "aNestedArray": ["a", "b", "c"] + } + +Which can be stored in a label by serializing it to a string first; + + LABEL com.example.image-specs="{\"Description\":\"A containerized foobar\",\"Usage\":\"docker run --rm example\\/foobar [args]\",\"License\":\"GPL\",\"Version\":\"0.0.1-beta\",\"aBoolean\":true,\"aNumber\":0.01234,\"aNestedArray\":[\"a\",\"b\",\"c\"]}" + + +> **Note:** Although the example above shows it's *possible* to store structured +> data in labels, Docker does not treat this data any other way than a 'regular' +> string. This means that Docker doesn't offer ways to query (filter) based on +> nested properties. If your tool needs to filter on nested properties, the +> tool itself should implement this. + + +### Adding labels to images; the `LABEL` instruction + +Adding labels to your + + + LABEL [.][=] ... + +The `LABEL` instruction adds a label to your image, optionally setting its value. +Quotes surrounding name and value are optional, but required if they contain +white space characters. Alternatively, backslashes can be used. + +Label values only support strings + + + LABEL vendor=ACME\ Incorporated + LABEL com.example.version.is-beta + LABEL com.example.version="0.0.1-beta" + LABEL com.example.release-date="2015-02-12" + +The `LABEL` instruction supports setting multiple labels in a single instruction +using this notation; + + LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12" + +Wrapping is allowed by using a backslash (`\`) as continuation marker; + + LABEL vendor=ACME\ Incorporated \ + com.example.is-beta \ + com.example.version="0.0.1-beta" \ + com.example.release-date="2015-02-12" + + +You can view the labels via `docker inspect` + + $ docker inspect 4fa6e0f0c678 + + ... + "Labels": { + "vendor": "ACME Incorporated", + "com.example.is-beta": "", + "com.example.version": "0.0.1-beta", + "com.example.release-date": "2015-02-12" + } + ... + + $ docker inspect -f "{{json .Labels }}" 4fa6e0f0c678 + + {"Vendor":"ACME Incorporated","com.example.is-beta":"","com.example.version":"0.0.1-beta","com.example.release-date":"2015-02-12"} + +> **note:** We recommend combining labels in a single `LABEl` instruction in +> stead of using a `LABEL` instruction for each label. Each instruction in a +> Dockerfile produces a new layer, which can result in an inefficient image if +> many labels are used. + +### Querying labels + +Besides storing meta-data, labels can be used to filter your images and +containers. + +List all running containers that have a `com.example.is-beta` label; + + # List all running containers that have a `com.example.is-beta` label + $ docker ps --filter "label=com.example.is-beta" + + +List all running containers with a "color" label set to "blue"; + + $ docker ps --filter "label=color=blue" + +List all images with "vendor" "ACME" + + $ docker images --filter "label=vendor=ACME" + + +### Daemon labels + + + + docker -d \ + --dns 8.8.8.8 \ + --dns 8.8.4.4 \ + -H unix:///var/run/docker.sock \ + --label com.example.environment="production" \ + --label com.example.storage="ssd" + +And can be seen as part of the `docker info` output for the daemon; + + docker -D info + Containers: 12 + Images: 672 + Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Backing Filesystem: extfs + Dirs: 697 + Execution Driver: native-0.2 + Kernel Version: 3.13.0-32-generic + Operating System: Ubuntu 14.04.1 LTS + CPUs: 1 + Total Memory: 994.1 MiB + Name: docker.example.com + ID: RC3P:JTCT:32YS:XYSB:YUBG:VFED:AAJZ:W3YW:76XO:D7NN:TEVU:UCRW + Debug mode (server): false + Debug mode (client): true + Fds: 11 + Goroutines: 14 + EventsListeners: 0 + Init Path: /usr/bin/docker + Docker Root Dir: /var/lib/docker + WARNING: No swap limit support + Labels: + com.example.environment=production + com.example.storage=ssd diff --git a/runconfig/parse.go b/runconfig/parse.go index cc638efc1b..284a13a4a7 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -76,7 +76,7 @@ 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(&flLabels, []string{"l", "-label"}, "Set meta data on a container") 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") From 5de1298f41ad0ab0b13c839854d6b2a5354294b8 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Sun, 8 Mar 2015 22:18:59 -0700 Subject: [PATCH 5/7] Add more tests for labels on containers Signed-off-by: Darren Shepherd --- integration-cli/docker_cli_ps_test.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index d6d8d2ab5c..c2e108576d 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -414,7 +414,7 @@ func TestPsListContainersFilterName(t *testing.T) { func TestPsListContainersFilterLabel(t *testing.T) { // start container - runCmd := exec.Command(dockerBinary, "run", "-d", "-l", "match=me", "busybox") + runCmd := exec.Command(dockerBinary, "run", "-d", "-l", "match=me", "-l", "second=tag", "busybox") out, _, err := runCommandWithOutput(runCmd) if err != nil { t.Fatal(out, err) @@ -445,6 +445,26 @@ func TestPsListContainersFilterLabel(t *testing.T) { t.Fatalf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out) } + // filter containers by two labels + runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me", "--filter=label=second=tag") + 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 two labels, but expect not found because of AND behavior + runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me", "--filter=label=second=tag-no") + if out, _, err = runCommandWithOutput(runCmd); err != nil { + t.Fatal(out, err) + } + containerOut = strings.TrimSpace(out) + if containerOut != "" { + t.Fatalf("Expected nothing, got %s for exited filter, output: %q", 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 { From 03cea0ef23595ec711fc6e702e249f3561afa36a Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Fri, 13 Mar 2015 09:38:41 -0700 Subject: [PATCH 6/7] Docs changes for labels based on feedback Signed-off-by: Darren Shepherd --- .../reference/api/docker_remote_api_v1.17.md | 17 ----------------- .../reference/api/docker_remote_api_v1.18.md | 17 +++++++++++++++++ docs/sources/reference/commandline/cli.md | 8 ++++---- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/sources/reference/api/docker_remote_api_v1.17.md b/docs/sources/reference/api/docker_remote_api_v1.17.md index d82faffe98..96887559c2 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.17.md +++ b/docs/sources/reference/api/docker_remote_api_v1.17.md @@ -129,11 +129,6 @@ Create a container ], "Entrypoint": "", "Image": "ubuntu", - "Labels": { - "com.example.vendor": "Acme", - "com.example.license": "GPL", - "com.example.version": "1.0" - }, "Volumes": { "/tmp": {} }, @@ -194,8 +189,6 @@ Json Parameters: - **OpenStdin** - Boolean value, opens stdin, - **StdinOnce** - Boolean value, close stdin after the 1 attached client disconnects. - **Env** - A list of environment variables in the form of `VAR=value` -- **Labels** - A map of labels and their values that will be added to the - container. It should be specified in the form `{"name":"value"[,"name2":"value2"]}` - **Cmd** - Command to run specified as a string or an array of strings. - **Entrypoint** - Set the entrypoint for the container a a string or an array of strings @@ -305,11 +298,6 @@ Return low-level information on the container `id` "ExposedPorts": null, "Hostname": "ba033ac44011", "Image": "ubuntu", - "Labels": { - "com.example.vendor": "Acme", - "com.example.license": "GPL", - "com.example.version": "1.0" - }, "MacAddress": "", "Memory": 0, "MemorySwap": 0, @@ -1181,11 +1169,6 @@ Return low-level information on the image `name` "Cmd": ["/bin/bash"], "Dns": null, "Image": "ubuntu", - "Labels": { - "com.example.vendor": "Acme", - "com.example.license": "GPL", - "com.example.version": "1.0" - }, "Volumes": null, "VolumesFrom": "", "WorkingDir": "" diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index 46351ed85c..fe6b59298d 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -129,6 +129,11 @@ Create a container ], "Entrypoint": "", "Image": "ubuntu", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, "Volumes": { "/tmp": {} }, @@ -190,6 +195,8 @@ Json Parameters: - **OpenStdin** - Boolean value, opens stdin, - **StdinOnce** - Boolean value, close stdin after the 1 attached client disconnects. - **Env** - A list of environment variables in the form of `VAR=value` +- **Labels** - A map of labels and their values that will be added to the + container. It should be specified in the form `{"name":"value"[,"name2":"value2"]}` - **Cmd** - Command to run specified as a string or an array of strings. - **Entrypoint** - Set the entrypoint for the container a a string or an array of strings @@ -302,6 +309,11 @@ Return low-level information on the container `id` "ExposedPorts": null, "Hostname": "ba033ac44011", "Image": "ubuntu", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, "MacAddress": "", "Memory": 0, "MemorySwap": 0, @@ -1186,6 +1198,11 @@ Return low-level information on the image `name` "Cmd": ["/bin/bash"], "Dns": null, "Image": "ubuntu", + "Labels": { + "com.example.vendor": "Acme", + "com.example.license": "GPL", + "com.example.version": "1.0" + }, "Volumes": null, "VolumesFrom": "", "WorkingDir": "" diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 79dbb6c8f5..284aefa12b 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -1840,21 +1840,21 @@ An example of a file passed with `--env-file` This will create and run a new container with the container name being `console`. - $ sudo docker run -l my-label --env com.example.foo=bar ubuntu bash + $ sudo docker run -l my-label --label com.example.foo=bar ubuntu bash This sets two labels on the container. Label "my-label" doesn't have a value specified and will default to "" (empty string) for its value. Both `-l` and -`--env` can be repeated to add more labels. Label names are unique; if the same +`--label` can be repeated to add more labels. Label names are unique; if the same label is specified multiple times, latter values overwrite the previous value. Labels can also be loaded from a line delimited file of labels using the `--label-file` flag. The example below will load labels from a file named `labels` in the current directory; - $ sudo docker run --env-file ./labels ubuntu bash + $ sudo docker run --label-file ./labels ubuntu bash The format of the labels-file is similar to that used for loading environment -variables (see `--env-file` above). An example of a file passed with `--env-file`; +variables (see `--label-file` above). An example of a file passed with `--label-file`; $ cat ./labels com.example.label1="a label" From fae92d5f0a1d81a48dc210d5ae28ef83c724b5ac Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Mon, 16 Mar 2015 13:28:55 -0700 Subject: [PATCH 7/7] Documentation changes for labels Signed-off-by: Darren Shepherd --- docs/man/Dockerfile.5.md | 17 ++- docs/man/docker-create.1.md | 4 +- docs/man/docker-images.1.md | 4 +- docs/man/docker-run.1.md | 2 +- docs/mkdocs.yml | 2 +- .../reference/api/docker_remote_api.md | 7 +- .../reference/api/docker_remote_api_v1.18.md | 3 +- docs/sources/reference/builder.md | 24 ++-- docs/sources/reference/commandline/cli.md | 39 +++--- .../userguide/labels-custom-metadata.md | 120 +++++++++--------- 10 files changed, 111 insertions(+), 111 deletions(-) diff --git a/docs/man/Dockerfile.5.md b/docs/man/Dockerfile.5.md index 48e39ac3c3..2f2b93686e 100644 --- a/docs/man/Dockerfile.5.md +++ b/docs/man/Dockerfile.5.md @@ -144,16 +144,19 @@ A Dockerfile is similar to a Makefile. the image. **LABEL** - --**LABEL [=] [[=] ...]** + -- `LABEL [=] [[=] ...]` + The **LABEL** instruction adds metadata to an image. A **LABEL** is a + key-value pair. To include spaces within a **LABEL** value, use quotes and + blackslashes as you would in command-line parsing. - The **LABEL** instruction allows you to add meta-data to the image your - Dockerfile is building. LABEL is specified as name value pairs. This data can - be retrieved using the `docker inspect` command. + ``` + LABEL "com.example.vendor"="ACME Incorporated" + ``` - The LABEL instruction allows for multiple labels to be set at one time. Like - command line parsing, quotes and backslashes can be used to include spaces - within values. + An image can have more than one label. To specify multiple labels, separate each + key-value pair by a space. + To display an image's labels, use the `docker inspect` command. **EXPOSE** -- `EXPOSE [...]` diff --git a/docs/man/docker-create.1.md b/docs/man/docker-create.1.md index 7fc7f98152..310b6d0ddc 100644 --- a/docs/man/docker-create.1.md +++ b/docs/man/docker-create.1.md @@ -105,10 +105,10 @@ IMAGE [COMMAND] [ARG...] 'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. **-l**, **--label**=[] - Set meta data on the container (e.g., --label=com.example.key=value) + Set metadata on the container (e.g., --label=com.example.key=value) **--label-file**=[] - Read in a line delimited file of labels + Read in a file of labels (EOL delimited) **--link**=[] Add link to another container in the form of :alias diff --git a/docs/man/docker-images.1.md b/docs/man/docker-images.1.md index 253bb07ce2..c82d2883f7 100644 --- a/docs/man/docker-images.1.md +++ b/docs/man/docker-images.1.md @@ -34,9 +34,7 @@ versions. Show all images (by default filter out the intermediate image layers). The default is *false*. **-f**, **--filter**=[] - Provide filter values. Valid filters: - dangling=true - unlabeled images with no children - label= or label== + Filters the output. The dangling=true filter finds unused images. While label=com.foo=amd64 filters for images with a com.foo value of amd64. The label=com.foo filter finds images with the label com.foo of any value. **--help** Print usage statement diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 4b405920c2..3402dc6ae7 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -200,7 +200,7 @@ ENTRYPOINT. 'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. **-l**, **--label**=[] - Set meta data on the container (e.g., --label=com.example.key=value) + Set metadata on the container (e.g., --label com.example.key=value) **--label-file**=[] Read in a line delimited file of labels diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 51dd388677..814f434394 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -59,7 +59,7 @@ pages: - ['userguide/dockerimages.md', 'User Guide', 'Working with Docker Images' ] - ['userguide/dockerlinks.md', 'User Guide', 'Linking containers together' ] - ['userguide/dockervolumes.md', 'User Guide', 'Managing data in containers' ] -- ['userguide/labels-custom-metadata.md', 'User Guide', 'Labels - custom meta-data in Docker' ] +- ['userguide/labels-custom-metadata.md', 'User Guide', 'Labels - custom metadata in Docker' ] - ['userguide/dockerrepos.md', 'User Guide', 'Working with Docker Hub' ] - ['userguide/level1.md', '**HIDDEN**' ] - ['userguide/level2.md', '**HIDDEN**' ] diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index 5e154b67e4..10c8c62aaa 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -71,9 +71,8 @@ This endpoint now returns `SystemTime`, `HttpProxy`,`HttpsProxy` and `NoProxy`. ### What's new -**New!** -Build now has support for `LABEL` command which can be used to add user data -to an image. For example you could add data describing the content of an image. +The build supports `LABEL` command. Use this to add metadata +to an image. For example you could add data describing the content of an image. `LABEL "com.example.vendor"="ACME Incorporated"` @@ -91,7 +90,7 @@ You can set labels on container create describing the container. `GET /containers/json` **New!** -This endpoint now returns the labels associated with each container (`Labels`). +The endpoint returns the labels associated with the containers (`Labels`). `GET /containers/(id)/json` diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index fe6b59298d..c7e1607aeb 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -195,8 +195,7 @@ Json Parameters: - **OpenStdin** - Boolean value, opens stdin, - **StdinOnce** - Boolean value, close stdin after the 1 attached client disconnects. - **Env** - A list of environment variables in the form of `VAR=value` -- **Labels** - A map of labels and their values that will be added to the - container. It should be specified in the form `{"name":"value"[,"name2":"value2"]}` +- **Labels** - Adds a map of labels that to a container. To specify a map: `{"key":"value"[,"key2":"value2"]}` - **Cmd** - Command to run specified as a string or an array of strings. - **Entrypoint** - Set the entrypoint for the container a a string or an array of strings diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index ff5821286d..9a03c516ee 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -329,26 +329,26 @@ default specified in `CMD`. > the intended command for the image. ## LABEL - LABEL = = = ... -The `LABEL` instruction allows you to add meta-data to the image your -`Dockerfile` is building. `LABEL` is specified as name value pairs. This data can -be retrieved using the `docker inspect` command + LABEL = = = ... + +The `LABEL` instruction adds metadata to an image. A `LABEL` is a +key-value pair. To include spaces within a `LABEL` value, use quotes and +blackslashes as you would in command-line parsing. + + LABEL "com.example.vendor"="ACME Incorporated" + +An image can have more than one label. To specify multiple labels, separate each +key-value pair by an EOL. LABEL com.example.label-without-value LABEL com.example.label-with-value="foo" LABEL version="1.0" - LABEL description="This my ACME image" vendor="ACME Products" - -As illustrated above, the `LABEL` instruction allows for multiple labels to be -set at one time. Like command line parsing, quotes and backslashes can be used -to include spaces within values. - -For example: - LABEL description="This text illustrates \ that label-values can span multiple lines." +To view an image's labels, use the `docker inspect` command. + ## EXPOSE EXPOSE [...] diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 284aefa12b..b8e579840d 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -791,7 +791,7 @@ Creates a new container. -h, --hostname="" Container host name -i, --interactive=false Keep STDIN open even if not attached --ipc="" IPC namespace to use - -l, --label=[] Set meta data on the container (e.g., --label=com.example.key=value) + -l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value) --label-file=[] Read in a line delimited file of labels --link=[] Add link to another container --lxc-conf=[] Add custom lxc options @@ -1665,8 +1665,8 @@ removed before the image is removed. --link=[] Add link to another container --lxc-conf=[] Add custom lxc options -m, --memory="" Memory limit - -l, --label=[] Set meta data on the container (e.g., --label=com.example.key=value) - --label-file=[] Read in a line delimited file of labels + -l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value) + --label-file=[] Read in a file of labels (EOL delimited) --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) --memory-swap="" Total memory (memory + swap), '-1' to disable swap --name="" Assign a name to the container @@ -1837,38 +1837,39 @@ An example of a file passed with `--env-file` $ sudo docker run --name console -t -i ubuntu bash -This will create and run a new container with the container name being -`console`. +A label is a a `key=value` pair that applies metadata to a container. To label a container with two labels: $ sudo docker run -l my-label --label com.example.foo=bar ubuntu bash -This sets two labels on the container. Label "my-label" doesn't have a value -specified and will default to "" (empty string) for its value. Both `-l` and -`--label` can be repeated to add more labels. Label names are unique; if the same -label is specified multiple times, latter values overwrite the previous value. +The `my-label` key doesn't specify so the label defaults to an empty +string(`""`). To add multiple labels, repeat the label flag (`-l` or +`--label`). -Labels can also be loaded from a line delimited file of labels using the -`--label-file` flag. The example below will load labels from a file named `labels` -in the current directory; +The `key=value` must be unique. If you specify the same key multiple times +with different values, each subsequent value overwrites the previous. Docker +applies the last `key=value` you supply. + +Use the `--label-file` flag to load multiple labels from a file. Delimit each +label in the file with an EOL mark. The example below loads labels from a +labels file in the current directory; $ sudo docker run --label-file ./labels ubuntu bash -The format of the labels-file is similar to that used for loading environment -variables (see `--label-file` above). An example of a file passed with `--label-file`; +The label-file format is similar to the format for loading environment variables +(see `--env-file` above). The following example illustrates a label-file format; - $ cat ./labels com.example.label1="a label" # this is a comment com.example.label2=another\ label com.example.label3 -Multiple label-files can be loaded by providing the `--label-file` multiple +You can load multiple label-files by supplying the `--label-file` flag multiple times. -For additional information on working with labels, see -[*Labels - custom meta-data in Docker*](/userguide/labels-custom-metadata/) in -the user guide. +For additional information on working with labels, see +[*Labels - custom metadata in Docker*](/userguide/labels-custom-metadata/) in +the Docker User Guide. $ sudo docker run --link /redis:redis --name console ubuntu bash diff --git a/docs/sources/userguide/labels-custom-metadata.md b/docs/sources/userguide/labels-custom-metadata.md index 0824af4d62..dc5c2407aa 100644 --- a/docs/sources/userguide/labels-custom-metadata.md +++ b/docs/sources/userguide/labels-custom-metadata.md @@ -1,61 +1,67 @@ -page_title: Labels - custom meta-data in Docker -page_description: Learn how to work with custom meta-data in Docker, using labels. -page_keywords: Usage, user guide, labels, meta-data, docker, documentation, examples, annotating +page_title: Labels - custom metadata in Docker +page_description: Learn how to work with custom metadata in Docker, using labels. +page_keywords: Usage, user guide, labels, metadata, docker, documentation, examples, annotating -## Labels - custom meta-data in Docker +## Labels - custom metadata in Docker -Docker enables you to add meta-data to your images, containers, and daemons via -labels. Meta-data can serve a wide range of uses ranging from adding notes or -licensing information to an image to identifying a host. +You can add metadata to your images, containers, and daemons via +labels. Metadata can serve a wide range of uses. Use them to add notes or +licensing information to an image or to identify a host. -Labels in Docker are implemented as `` / `` pairs and values are -stored as *strings*. +A label is a `` / `` pair. Docker stores the values as *strings*. +You can specify multiple labels but each `` / `` must be unique. If +you specify the same `key` multiple times with different values, each subsequent +value overwrites the previous. Docker applies the last `key=value` you supply. ->**note:** Support for daemon-labels was added in docker 1.4.1, labels on ->containers and images in docker 1.6.0 +>**note:** Support for daemon-labels was added in Docker 1.4.1. Labels on +>containers and images are new in Docker 1.6.0 ### Naming your labels - namespaces -Docker puts no hard restrictions on the names you pick for your labels, however, -it's easy for labels to "conflict". - -For example, you're building a tool that categorizes your images in -architectures, doing so by using an "architecture" label; +Docker puts no hard restrictions on the label `key` you. However, labels can +conflict. For example, you can categorize your images by using a chip "architecture" +label: LABEL architecture="amd64" LABEL architecture="ARMv7" -But a user decided to label images by Architectural style +But a user can label images by building architectural style: LABEL architecture="Art Nouveau" -To prevent such conflicts, Docker uses the convention to namespace label-names, -using a reverse domain notation; +To prevent such conflicts, Docker namespaces label keys using a reverse domain +notation. This notation has the following guidelines: -- All (third-party) tools should namespace (prefix) their labels with the - reverse DNS notation of a domain controlled by the author of the tool. For - example, "com.example.some-label". -- Namespaced labels should only consist of lower-cased alphanumeric characters, - dots and dashes (in short, `[a-z0-9-.]`), should start *and* end with an alpha - numeric character, and may not contain consecutive dots or dashes. +- All (third-party) tools should prefix their keys with the + reverse DNS notation of a domain controlled by the author. For + example, `com.example.some-label`. + - The `com.docker.*`, `io.docker.*` and `com.dockerproject.*` namespaces are reserved for Docker's internal use. -- Labels *without* namespace (dots) are reserved for CLI use. This allows end- - users to add meta-data to their containers and images, without having to type + +- Keys should only consist of lower-cased alphanumeric characters, + dots and dashes (for example, `[a-z0-9-.]`) + +- Keys should start *and* end with an alpha numeric character + +- Keys may not contain consecutive dots or dashes. + +- Keys *without* namespace (dots) are reserved for CLI use. This allows end- + users to add metadata to their containers and images, without having to type cumbersome namespaces on the command-line. -> **Note:** Even though Docker does not *enforce* you to use namespaces, -> preventing to do so will likely result in conflicting usage of labels. -> If you're building a tool that uses labels, you *should* use namespaces -> for your labels. +These are guidelines and are not enforced. Docker does not *enforce* them. +Failing following these guidelines can result in conflicting labels. If you're +building a tool that uses labels, you *should* use namespaces for your label keys. ### Storing structured data in labels -Labels can store any type of data, as long as its stored as a string. +Label values can contain any data type as long as the value can be stored as a +string. For example, consider this JSON: { @@ -68,31 +74,28 @@ Labels can store any type of data, as long as its stored as a string. "aNestedArray": ["a", "b", "c"] } -Which can be stored in a label by serializing it to a string first; +You can store this struct in a label by serializing it to a string first: LABEL com.example.image-specs="{\"Description\":\"A containerized foobar\",\"Usage\":\"docker run --rm example\\/foobar [args]\",\"License\":\"GPL\",\"Version\":\"0.0.1-beta\",\"aBoolean\":true,\"aNumber\":0.01234,\"aNestedArray\":[\"a\",\"b\",\"c\"]}" +While it is *possible* to store structured data in label values, Docker treats this +data as a 'regular' string. This means that Docker doesn't offer ways to query +(filter) based on nested properties. -> **Note:** Although the example above shows it's *possible* to store structured -> data in labels, Docker does not treat this data any other way than a 'regular' -> string. This means that Docker doesn't offer ways to query (filter) based on -> nested properties. If your tool needs to filter on nested properties, the -> tool itself should implement this. +If your tool needs to filter on nested properties, the tool itself should +implement this. ### Adding labels to images; the `LABEL` instruction -Adding labels to your +Adding labels to an image: - LABEL [.][=] ... + LABEL [.][=] ... The `LABEL` instruction adds a label to your image, optionally setting its value. -Quotes surrounding name and value are optional, but required if they contain -white space characters. Alternatively, backslashes can be used. - -Label values only support strings - +Use surrounding quotes or backslashes for labels that contain +white space character: LABEL vendor=ACME\ Incorporated LABEL com.example.version.is-beta @@ -104,15 +107,19 @@ using this notation; LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12" -Wrapping is allowed by using a backslash (`\`) as continuation marker; +Wrapping is allowed by using a backslash (`\`) as continuation marker: LABEL vendor=ACME\ Incorporated \ com.example.is-beta \ com.example.version="0.0.1-beta" \ com.example.release-date="2015-02-12" +Docker recommends combining labels in a single `LABEL` instruction instead of +using a `LABEL` instruction for each label. Each instruction in a Dockerfile +produces a new layer that can result in an inefficient image if you use many +labels. -You can view the labels via `docker inspect` +You can view the labels via the `docker inspect` command: $ docker inspect 4fa6e0f0c678 @@ -129,27 +136,21 @@ You can view the labels via `docker inspect` {"Vendor":"ACME Incorporated","com.example.is-beta":"","com.example.version":"0.0.1-beta","com.example.release-date":"2015-02-12"} -> **note:** We recommend combining labels in a single `LABEl` instruction in -> stead of using a `LABEL` instruction for each label. Each instruction in a -> Dockerfile produces a new layer, which can result in an inefficient image if -> many labels are used. + ### Querying labels -Besides storing meta-data, labels can be used to filter your images and -containers. - -List all running containers that have a `com.example.is-beta` label; +Besides storing metadata, you can filter images and labels by label. To list all +running containers that have a `com.example.is-beta` label: # List all running containers that have a `com.example.is-beta` label $ docker ps --filter "label=com.example.is-beta" - -List all running containers with a "color" label set to "blue"; +List all running containers with a `color` label of `blue`: $ docker ps --filter "label=color=blue" -List all images with "vendor" "ACME" +List all images with `vendor` `ACME`: $ docker images --filter "label=vendor=ACME" @@ -157,7 +158,6 @@ List all images with "vendor" "ACME" ### Daemon labels - docker -d \ --dns 8.8.8.8 \ --dns 8.8.4.4 \ @@ -165,7 +165,7 @@ List all images with "vendor" "ACME" --label com.example.environment="production" \ --label com.example.storage="ssd" -And can be seen as part of the `docker info` output for the daemon; +These labels appear as part of the `docker info` output for the daemon: docker -D info Containers: 12