10 "github.com/Sirupsen/logrus"
11 "github.com/docker/cli/cli"
12 "github.com/docker/docker/pkg/templates"
13 "github.com/pkg/errors"
16 // Inspector defines an interface to implement to process elements
17 type Inspector interface {
18 Inspect(typedElement interface{}, rawElement []byte) error
22 // TemplateInspector uses a text template to inspect elements.
23 type TemplateInspector struct {
24 outputStream io.Writer
26 tmpl *template.Template
29 // NewTemplateInspector creates a new inspector with a template.
30 func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
31 return &TemplateInspector{
32 outputStream: outputStream,
33 buffer: new(bytes.Buffer),
38 // NewTemplateInspectorFromString creates a new TemplateInspector from a string
39 // which is compiled into a template.
40 func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, error) {
42 return NewIndentedInspector(out), nil
45 tmpl, err := templates.Parse(tmplStr)
47 return nil, errors.Errorf("Template parsing error: %s", err)
49 return NewTemplateInspector(out, tmpl), nil
52 // GetRefFunc is a function which used by Inspect to fetch an object from a
54 type GetRefFunc func(ref string) (interface{}, []byte, error)
56 // Inspect fetches objects by reference using GetRefFunc and writes the json
57 // representation to the output writer.
58 func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFunc) error {
59 inspector, err := NewTemplateInspectorFromString(out, tmplStr)
61 return cli.StatusError{StatusCode: 64, Status: err.Error()}
64 var inspectErrs []string
65 for _, ref := range references {
66 element, raw, err := getRef(ref)
68 inspectErrs = append(inspectErrs, err.Error())
72 if err := inspector.Inspect(element, raw); err != nil {
73 inspectErrs = append(inspectErrs, err.Error())
77 if err := inspector.Flush(); err != nil {
78 logrus.Errorf("%s\n", err)
81 if len(inspectErrs) != 0 {
82 return cli.StatusError{
84 Status: strings.Join(inspectErrs, "\n"),
90 // Inspect executes the inspect template.
91 // It decodes the raw element into a map if the initial execution fails.
92 // This allows docker cli to parse inspect structs injected with Swarm fields.
93 func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
94 buffer := new(bytes.Buffer)
95 if err := i.tmpl.Execute(buffer, typedElement); err != nil {
96 if rawElement == nil {
97 return errors.Errorf("Template parsing error: %v", err)
99 return i.tryRawInspectFallback(rawElement)
101 i.buffer.Write(buffer.Bytes())
102 i.buffer.WriteByte('\n')
106 // tryRawInspectFallback executes the inspect template with a raw interface.
107 // This allows docker cli to parse inspect structs injected with Swarm fields.
108 func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
110 buffer := new(bytes.Buffer)
111 rdr := bytes.NewReader(rawElement)
112 dec := json.NewDecoder(rdr)
115 if rawErr := dec.Decode(&raw); rawErr != nil {
116 return errors.Errorf("unable to read inspect data: %v", rawErr)
119 tmplMissingKey := i.tmpl.Option("missingkey=error")
120 if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
121 return errors.Errorf("Template parsing error: %v", rawErr)
124 i.buffer.Write(buffer.Bytes())
125 i.buffer.WriteByte('\n')
129 // Flush writes the result of inspecting all elements into the output stream.
130 func (i *TemplateInspector) Flush() error {
131 if i.buffer.Len() == 0 {
132 _, err := io.WriteString(i.outputStream, "\n")
135 _, err := io.Copy(i.outputStream, i.buffer)
139 // IndentedInspector uses a buffer to stop the indented representation of an element.
140 type IndentedInspector struct {
141 outputStream io.Writer
142 elements []interface{}
146 // NewIndentedInspector generates a new IndentedInspector.
147 func NewIndentedInspector(outputStream io.Writer) Inspector {
148 return &IndentedInspector{
149 outputStream: outputStream,
153 // Inspect writes the raw element with an indented json format.
154 func (i *IndentedInspector) Inspect(typedElement interface{}, rawElement []byte) error {
155 if rawElement != nil {
156 i.rawElements = append(i.rawElements, rawElement)
158 i.elements = append(i.elements, typedElement)
163 // Flush writes the result of inspecting all elements into the output stream.
164 func (i *IndentedInspector) Flush() error {
165 if len(i.elements) == 0 && len(i.rawElements) == 0 {
166 _, err := io.WriteString(i.outputStream, "[]\n")
171 if len(i.rawElements) > 0 {
172 bytesBuffer := new(bytes.Buffer)
173 bytesBuffer.WriteString("[")
174 for idx, r := range i.rawElements {
176 if idx < len(i.rawElements)-1 {
177 bytesBuffer.WriteString(",")
180 bytesBuffer.WriteString("]")
181 indented := new(bytes.Buffer)
182 if err := json.Indent(indented, bytesBuffer.Bytes(), "", " "); err != nil {
187 b, err := json.MarshalIndent(i.elements, "", " ")
191 buffer = bytes.NewReader(b)
194 if _, err := io.Copy(i.outputStream, buffer); err != nil {
197 _, err := io.WriteString(i.outputStream, "\n")