1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Provide basic string manupilation functions for template executions.

This change centralizes the template manipulation in a single package
and adds basic string functions to their execution.

Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2016-03-04 12:29:44 -05:00
parent 44b56341d9
commit 8514880997
9 changed files with 155 additions and 23 deletions

View file

@ -9,6 +9,7 @@ import (
"text/template" "text/template"
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
"github.com/docker/docker/utils/templates"
"github.com/docker/engine-api/types" "github.com/docker/engine-api/types"
) )
@ -54,7 +55,7 @@ func (c *Context) preformat() {
} }
func (c *Context) parseFormat() (*template.Template, error) { func (c *Context) parseFormat() (*template.Template, error) {
tmpl, err := template.New("").Parse(c.finalFormat) tmpl, err := templates.Parse(c.finalFormat)
if err != nil { if err != nil {
c.buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err)) c.buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err))
c.buffer.WriteTo(c.Output) c.buffer.WriteTo(c.Output)

View file

@ -1,23 +1,15 @@
package client package client
import ( import (
"encoding/json"
"fmt" "fmt"
"text/template"
"github.com/docker/docker/api/client/inspect" "github.com/docker/docker/api/client/inspect"
Cli "github.com/docker/docker/cli" Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag" flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/utils/templates"
"github.com/docker/engine-api/client" "github.com/docker/engine-api/client"
) )
var funcMap = template.FuncMap{
"json": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}
// CmdInspect displays low-level information on one or more containers or images. // CmdInspect displays low-level information on one or more containers or images.
// //
// Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...] // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
@ -123,7 +115,7 @@ func (cli *DockerCli) inspectErrorStatus(err error) (status int) {
func (cli *DockerCli) newInspectorWithTemplate(tmplStr string) (inspect.Inspector, error) { func (cli *DockerCli) newInspectorWithTemplate(tmplStr string) (inspect.Inspector, error) {
elementInspector := inspect.NewIndentedInspector(cli.out) elementInspector := inspect.NewIndentedInspector(cli.out)
if tmplStr != "" { if tmplStr != "" {
tmpl, err := template.New("").Funcs(funcMap).Parse(tmplStr) tmpl, err := templates.Parse(tmplStr)
if err != nil { if err != nil {
return nil, fmt.Errorf("Template parsing error: %s", err) return nil, fmt.Errorf("Template parsing error: %s", err)
} }

View file

@ -4,7 +4,8 @@ import (
"bytes" "bytes"
"strings" "strings"
"testing" "testing"
"text/template"
"github.com/docker/docker/utils/templates"
) )
type testElement struct { type testElement struct {
@ -13,7 +14,7 @@ type testElement struct {
func TestTemplateInspectorDefault(t *testing.T) { func TestTemplateInspectorDefault(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.DNS}}") tmpl, err := templates.Parse("{{.DNS}}")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -32,7 +33,7 @@ func TestTemplateInspectorDefault(t *testing.T) {
func TestTemplateInspectorEmpty(t *testing.T) { func TestTemplateInspectorEmpty(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.DNS}}") tmpl, err := templates.Parse("{{.DNS}}")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -48,7 +49,7 @@ func TestTemplateInspectorEmpty(t *testing.T) {
func TestTemplateInspectorTemplateError(t *testing.T) { func TestTemplateInspectorTemplateError(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.Foo}}") tmpl, err := templates.Parse("{{.Foo}}")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -66,7 +67,7 @@ func TestTemplateInspectorTemplateError(t *testing.T) {
func TestTemplateInspectorRawFallback(t *testing.T) { func TestTemplateInspectorRawFallback(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.Dns}}") tmpl, err := templates.Parse("{{.Dns}}")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -85,7 +86,7 @@ func TestTemplateInspectorRawFallback(t *testing.T) {
func TestTemplateInspectorRawFallbackError(t *testing.T) { func TestTemplateInspectorRawFallbackError(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.Dns}}") tmpl, err := templates.Parse("{{.Dns}}")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -102,7 +103,7 @@ func TestTemplateInspectorRawFallbackError(t *testing.T) {
func TestTemplateInspectorMultiple(t *testing.T) { func TestTemplateInspectorMultiple(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.DNS}}") tmpl, err := templates.Parse("{{.DNS}}")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/docker/dockerversion" "github.com/docker/docker/dockerversion"
flag "github.com/docker/docker/pkg/mflag" flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/utils" "github.com/docker/docker/utils"
"github.com/docker/docker/utils/templates"
"github.com/docker/engine-api/types" "github.com/docker/engine-api/types"
) )
@ -48,7 +49,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
} }
var tmpl *template.Template var tmpl *template.Template
if tmpl, err = template.New("").Funcs(funcMap).Parse(templateFormat); err != nil { if tmpl, err = templates.Parse(templateFormat); err != nil {
return Cli.StatusError{StatusCode: 64, return Cli.StatusError{StatusCode: 64,
Status: "Template parsing error: " + err.Error()} Status: "Template parsing error: " + err.Error()}
} }

View file

@ -3,10 +3,10 @@ package loggerutils
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"text/template"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger"
"github.com/docker/docker/utils/templates"
) )
// ParseLogTag generates a context aware tag for consistency across different // ParseLogTag generates a context aware tag for consistency across different
@ -14,7 +14,7 @@ import (
func ParseLogTag(ctx logger.Context, defaultTemplate string) (string, error) { func ParseLogTag(ctx logger.Context, defaultTemplate string) (string, error) {
tagTemplate := lookupTagTemplate(ctx, defaultTemplate) tagTemplate := lookupTagTemplate(ctx, defaultTemplate)
tmpl, err := template.New("log-tag").Parse(tagTemplate) tmpl, err := templates.NewParse("log-tag", tagTemplate)
if err != nil { if err != nil {
return "", err return "", err
} }

66
docs/admin/formatting.md Normal file
View file

@ -0,0 +1,66 @@
<!--[metadata]>
+++
title = "Format command and log output"
description = "CLI and log output formatting reference"
keywords = ["format, formatting, output, templates, log"]
[menu.main]
parent = "engine_admin"
weight=-90
+++
<![end-metadata]-->
# Formatting reference
Docker uses [Go templates](https://golang.org/pkg/text/template/) to allow users manipulate the output format
of certain commands and log drivers. Each command a driver provides a detailed
list of elements they support in their templates:
- [Docker Images formatting](https://docs.docker.com/engine/reference/commandline/images/#formatting)
- [Docker Inspect formatting](https://docs.docker.com/engine/reference/commandline/inspect/#examples)
- [Docker Log Tag formatting](https://docs.docker.com/engine/admin/logging/log_tags/)
- [Docker Network Inspect formatting](https://docs.docker.com/engine/reference/commandline/network_inspect/)
- [Docker PS formatting](https://docs.docker.com/engine/reference/commandline/ps/#formatting)
- [Docker Volume Inspect formatting](https://docs.docker.com/engine/reference/commandline/volume_inspect/)
- [Docker Version formatting](https://docs.docker.com/engine/reference/commandline/version/#examples)
## Template functions
Docker provides a set of basic functions to manipulate template elements.
This is the complete list of the available functions with examples:
### Join
Join concatenates a list of strings to create a single string.
It puts a separator between each element in the list.
$ docker ps --format '{{join .Names " or "}}'
### Json
Json encodes an element as a json string.
$ docker inspect --format '{{json .Mounts}}' container
### Lower
Lower turns a string into its lower case representation.
$ docker inspect --format "{{lower .Name}}" container
### Split
Split slices a string into a list of strings separated by a separator.
# docker inspect --format '{{split (join .Names "/") "/"}}' container
### Title
Title capitalizes a string.
$ docker inspect --format "{{title .Name}}" container
### Upper
Upper turms a string into its upper case representation.
$ docker inspect --format "{{upper .Name}}" container

View file

@ -8,9 +8,9 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"text/template"
"github.com/docker/docker/pkg/aaparser" "github.com/docker/docker/pkg/aaparser"
"github.com/docker/docker/utils/templates"
) )
var ( var (
@ -36,7 +36,7 @@ type profileData struct {
// generateDefault creates an apparmor profile from ProfileData. // generateDefault creates an apparmor profile from ProfileData.
func (p *profileData) generateDefault(out io.Writer) error { func (p *profileData) generateDefault(out io.Writer) error {
compiled, err := template.New("apparmor_profile").Parse(baseTemplate) compiled, err := templates.NewParse("apparmor_profile", baseTemplate)
if err != nil { if err != nil {
return err return err
} }

View file

@ -0,0 +1,33 @@
package templates
import (
"encoding/json"
"strings"
"text/template"
)
// basicFunctions are the set of initial
// functions provided to every template.
var basicFunctions = template.FuncMap{
"json": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
"split": strings.Split,
"join": strings.Join,
"title": strings.Title,
"lower": strings.ToLower,
"upper": strings.ToUpper,
}
// Parse creates a new annonymous template with the basic functions
// and parses the given format.
func Parse(format string) (*template.Template, error) {
return NewParse("", format)
}
// NewParse creates a new tagged template with the basic functions
// and parses the given format.
func NewParse(tag, format string) (*template.Template, error) {
return template.New(tag).Funcs(basicFunctions).Parse(format)
}

View file

@ -0,0 +1,38 @@
package templates
import (
"bytes"
"testing"
)
func TestParseStringFunctions(t *testing.T) {
tm, err := Parse(`{{join (split . ":") "/"}}`)
if err != nil {
t.Fatal(err)
}
var b bytes.Buffer
if err := tm.Execute(&b, "text:with:colon"); err != nil {
t.Fatal(err)
}
want := "text/with/colon"
if b.String() != want {
t.Fatalf("expected %s, got %s", want, b.String())
}
}
func TestNewParse(t *testing.T) {
tm, err := NewParse("foo", "this is a {{ . }}")
if err != nil {
t.Fatal(err)
}
var b bytes.Buffer
if err := tm.Execute(&b, "string"); err != nil {
t.Fatal(err)
}
want := "this is a string"
if b.String() != want {
t.Fatalf("expected %s, got %s", want, b.String())
}
}