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:
parent
44b56341d9
commit
8514880997
9 changed files with 155 additions and 23 deletions
|
@ -9,6 +9,7 @@ import (
|
|||
"text/template"
|
||||
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
|
@ -54,7 +55,7 @@ func (c *Context) preformat() {
|
|||
}
|
||||
|
||||
func (c *Context) parseFormat() (*template.Template, error) {
|
||||
tmpl, err := template.New("").Parse(c.finalFormat)
|
||||
tmpl, err := templates.Parse(c.finalFormat)
|
||||
if err != nil {
|
||||
c.buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err))
|
||||
c.buffer.WriteTo(c.Output)
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/api/client/inspect"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"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.
|
||||
//
|
||||
// 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) {
|
||||
elementInspector := inspect.NewIndentedInspector(cli.out)
|
||||
if tmplStr != "" {
|
||||
tmpl, err := template.New("").Funcs(funcMap).Parse(tmplStr)
|
||||
tmpl, err := templates.Parse(tmplStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Template parsing error: %s", err)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import (
|
|||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/utils/templates"
|
||||
)
|
||||
|
||||
type testElement struct {
|
||||
|
@ -13,7 +14,7 @@ type testElement struct {
|
|||
|
||||
func TestTemplateInspectorDefault(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := template.New("test").Parse("{{.DNS}}")
|
||||
tmpl, err := templates.Parse("{{.DNS}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -32,7 +33,7 @@ func TestTemplateInspectorDefault(t *testing.T) {
|
|||
|
||||
func TestTemplateInspectorEmpty(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := template.New("test").Parse("{{.DNS}}")
|
||||
tmpl, err := templates.Parse("{{.DNS}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -48,7 +49,7 @@ func TestTemplateInspectorEmpty(t *testing.T) {
|
|||
|
||||
func TestTemplateInspectorTemplateError(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := template.New("test").Parse("{{.Foo}}")
|
||||
tmpl, err := templates.Parse("{{.Foo}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -66,7 +67,7 @@ func TestTemplateInspectorTemplateError(t *testing.T) {
|
|||
|
||||
func TestTemplateInspectorRawFallback(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := template.New("test").Parse("{{.Dns}}")
|
||||
tmpl, err := templates.Parse("{{.Dns}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ func TestTemplateInspectorRawFallback(t *testing.T) {
|
|||
|
||||
func TestTemplateInspectorRawFallbackError(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := template.New("test").Parse("{{.Dns}}")
|
||||
tmpl, err := templates.Parse("{{.Dns}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ func TestTemplateInspectorRawFallbackError(t *testing.T) {
|
|||
|
||||
func TestTemplateInspectorMultiple(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := template.New("test").Parse("{{.DNS}}")
|
||||
tmpl, err := templates.Parse("{{.DNS}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/docker/docker/dockerversion"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
|
@ -48,7 +49,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
|||
}
|
||||
|
||||
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,
|
||||
Status: "Template parsing error: " + err.Error()}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ package loggerutils
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
)
|
||||
|
||||
// ParseLogTag generates a context aware tag for consistency across different
|
||||
|
@ -14,7 +14,7 @@ import (
|
|||
func ParseLogTag(ctx logger.Context, defaultTemplate string) (string, error) {
|
||||
tagTemplate := lookupTagTemplate(ctx, defaultTemplate)
|
||||
|
||||
tmpl, err := template.New("log-tag").Parse(tagTemplate)
|
||||
tmpl, err := templates.NewParse("log-tag", tagTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
66
docs/admin/formatting.md
Normal file
66
docs/admin/formatting.md
Normal 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
|
|
@ -8,9 +8,9 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/pkg/aaparser"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -36,7 +36,7 @@ type profileData struct {
|
|||
|
||||
// generateDefault creates an apparmor profile from ProfileData.
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
33
utils/templates/templates.go
Normal file
33
utils/templates/templates.go
Normal 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)
|
||||
}
|
38
utils/templates/templates_test.go
Normal file
38
utils/templates/templates_test.go
Normal 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())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue