Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / docker / cli / cli / command / inspect / inspector.go
1 package inspect
2
3 import (
4         "bytes"
5         "encoding/json"
6         "io"
7         "strings"
8         "text/template"
9
10         "github.com/Sirupsen/logrus"
11         "github.com/docker/cli/cli"
12         "github.com/docker/docker/pkg/templates"
13         "github.com/pkg/errors"
14 )
15
16 // Inspector defines an interface to implement to process elements
17 type Inspector interface {
18         Inspect(typedElement interface{}, rawElement []byte) error
19         Flush() error
20 }
21
22 // TemplateInspector uses a text template to inspect elements.
23 type TemplateInspector struct {
24         outputStream io.Writer
25         buffer       *bytes.Buffer
26         tmpl         *template.Template
27 }
28
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),
34                 tmpl:         tmpl,
35         }
36 }
37
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) {
41         if tmplStr == "" {
42                 return NewIndentedInspector(out), nil
43         }
44
45         tmpl, err := templates.Parse(tmplStr)
46         if err != nil {
47                 return nil, errors.Errorf("Template parsing error: %s", err)
48         }
49         return NewTemplateInspector(out, tmpl), nil
50 }
51
52 // GetRefFunc is a function which used by Inspect to fetch an object from a
53 // reference
54 type GetRefFunc func(ref string) (interface{}, []byte, error)
55
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)
60         if err != nil {
61                 return cli.StatusError{StatusCode: 64, Status: err.Error()}
62         }
63
64         var inspectErrs []string
65         for _, ref := range references {
66                 element, raw, err := getRef(ref)
67                 if err != nil {
68                         inspectErrs = append(inspectErrs, err.Error())
69                         continue
70                 }
71
72                 if err := inspector.Inspect(element, raw); err != nil {
73                         inspectErrs = append(inspectErrs, err.Error())
74                 }
75         }
76
77         if err := inspector.Flush(); err != nil {
78                 logrus.Errorf("%s\n", err)
79         }
80
81         if len(inspectErrs) != 0 {
82                 return cli.StatusError{
83                         StatusCode: 1,
84                         Status:     strings.Join(inspectErrs, "\n"),
85                 }
86         }
87         return nil
88 }
89
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)
98                 }
99                 return i.tryRawInspectFallback(rawElement)
100         }
101         i.buffer.Write(buffer.Bytes())
102         i.buffer.WriteByte('\n')
103         return nil
104 }
105
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 {
109         var raw interface{}
110         buffer := new(bytes.Buffer)
111         rdr := bytes.NewReader(rawElement)
112         dec := json.NewDecoder(rdr)
113         dec.UseNumber()
114
115         if rawErr := dec.Decode(&raw); rawErr != nil {
116                 return errors.Errorf("unable to read inspect data: %v", rawErr)
117         }
118
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)
122         }
123
124         i.buffer.Write(buffer.Bytes())
125         i.buffer.WriteByte('\n')
126         return nil
127 }
128
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")
133                 return err
134         }
135         _, err := io.Copy(i.outputStream, i.buffer)
136         return err
137 }
138
139 // IndentedInspector uses a buffer to stop the indented representation of an element.
140 type IndentedInspector struct {
141         outputStream io.Writer
142         elements     []interface{}
143         rawElements  [][]byte
144 }
145
146 // NewIndentedInspector generates a new IndentedInspector.
147 func NewIndentedInspector(outputStream io.Writer) Inspector {
148         return &IndentedInspector{
149                 outputStream: outputStream,
150         }
151 }
152
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)
157         } else {
158                 i.elements = append(i.elements, typedElement)
159         }
160         return nil
161 }
162
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")
167                 return err
168         }
169
170         var buffer io.Reader
171         if len(i.rawElements) > 0 {
172                 bytesBuffer := new(bytes.Buffer)
173                 bytesBuffer.WriteString("[")
174                 for idx, r := range i.rawElements {
175                         bytesBuffer.Write(r)
176                         if idx < len(i.rawElements)-1 {
177                                 bytesBuffer.WriteString(",")
178                         }
179                 }
180                 bytesBuffer.WriteString("]")
181                 indented := new(bytes.Buffer)
182                 if err := json.Indent(indented, bytesBuffer.Bytes(), "", "    "); err != nil {
183                         return err
184                 }
185                 buffer = indented
186         } else {
187                 b, err := json.MarshalIndent(i.elements, "", "    ")
188                 if err != nil {
189                         return err
190                 }
191                 buffer = bytes.NewReader(b)
192         }
193
194         if _, err := io.Copy(i.outputStream, buffer); err != nil {
195                 return err
196         }
197         _, err := io.WriteString(i.outputStream, "\n")
198         return err
199 }