123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- package inspect
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "text/template"
- "github.com/Sirupsen/logrus"
- "github.com/docker/docker/cli"
- "github.com/docker/docker/utils/templates"
- )
- // Inspector defines an interface to implement to process elements
- type Inspector interface {
- Inspect(typedElement interface{}, rawElement []byte) error
- Flush() error
- }
- // TemplateInspector uses a text template to inspect elements.
- type TemplateInspector struct {
- outputStream io.Writer
- buffer *bytes.Buffer
- tmpl *template.Template
- }
- // NewTemplateInspector creates a new inspector with a template.
- func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
- return &TemplateInspector{
- outputStream: outputStream,
- buffer: new(bytes.Buffer),
- tmpl: tmpl,
- }
- }
- // NewTemplateInspectorFromString creates a new TemplateInspector from a string
- // which is compiled into a template.
- func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, error) {
- if tmplStr == "" {
- return NewIndentedInspector(out), nil
- }
- tmpl, err := templates.Parse(tmplStr)
- if err != nil {
- return nil, fmt.Errorf("Template parsing error: %s", err)
- }
- return NewTemplateInspector(out, tmpl), nil
- }
- // GetRefFunc is a function which used by Inspect to fetch an object from a
- // reference
- type GetRefFunc func(ref string) (interface{}, []byte, error)
- // Inspect fetches objects by reference using GetRefFunc and writes the json
- // representation to the output writer.
- func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFunc) error {
- inspector, err := NewTemplateInspectorFromString(out, tmplStr)
- if err != nil {
- return cli.StatusError{StatusCode: 64, Status: err.Error()}
- }
- var inspectErr error
- for _, ref := range references {
- element, raw, err := getRef(ref)
- if err != nil {
- inspectErr = err
- break
- }
- if err := inspector.Inspect(element, raw); err != nil {
- inspectErr = err
- break
- }
- }
- if err := inspector.Flush(); err != nil {
- logrus.Errorf("%s\n", err)
- }
- if inspectErr != nil {
- return cli.StatusError{StatusCode: 1, Status: inspectErr.Error()}
- }
- return nil
- }
- // Inspect executes the inspect template.
- // It decodes the raw element into a map if the initial execution fails.
- // This allows docker cli to parse inspect structs injected with Swarm fields.
- func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
- buffer := new(bytes.Buffer)
- if err := i.tmpl.Execute(buffer, typedElement); err != nil {
- if rawElement == nil {
- return fmt.Errorf("Template parsing error: %v", err)
- }
- return i.tryRawInspectFallback(rawElement)
- }
- i.buffer.Write(buffer.Bytes())
- i.buffer.WriteByte('\n')
- return nil
- }
- // tryRawInspectFallback executes the inspect template with a raw interface.
- // This allows docker cli to parse inspect structs injected with Swarm fields.
- func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
- var raw interface{}
- buffer := new(bytes.Buffer)
- rdr := bytes.NewReader(rawElement)
- dec := json.NewDecoder(rdr)
- if rawErr := dec.Decode(&raw); rawErr != nil {
- return fmt.Errorf("unable to read inspect data: %v", rawErr)
- }
- tmplMissingKey := i.tmpl.Option("missingkey=error")
- if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
- return fmt.Errorf("Template parsing error: %v", rawErr)
- }
- i.buffer.Write(buffer.Bytes())
- i.buffer.WriteByte('\n')
- return nil
- }
- // Flush write the result of inspecting all elements into the output stream.
- func (i *TemplateInspector) Flush() error {
- if i.buffer.Len() == 0 {
- _, err := io.WriteString(i.outputStream, "\n")
- return err
- }
- _, err := io.Copy(i.outputStream, i.buffer)
- return err
- }
- // IndentedInspector uses a buffer to stop the indented representation of an element.
- type IndentedInspector struct {
- outputStream io.Writer
- elements []interface{}
- rawElements [][]byte
- }
- // NewIndentedInspector generates a new IndentedInspector.
- func NewIndentedInspector(outputStream io.Writer) Inspector {
- return &IndentedInspector{
- outputStream: outputStream,
- }
- }
- // Inspect writes the raw element with an indented json format.
- func (i *IndentedInspector) Inspect(typedElement interface{}, rawElement []byte) error {
- if rawElement != nil {
- i.rawElements = append(i.rawElements, rawElement)
- } else {
- i.elements = append(i.elements, typedElement)
- }
- return nil
- }
- // Flush write the result of inspecting all elements into the output stream.
- func (i *IndentedInspector) Flush() error {
- if len(i.elements) == 0 && len(i.rawElements) == 0 {
- _, err := io.WriteString(i.outputStream, "[]\n")
- return err
- }
- var buffer io.Reader
- if len(i.rawElements) > 0 {
- bytesBuffer := new(bytes.Buffer)
- bytesBuffer.WriteString("[")
- for idx, r := range i.rawElements {
- bytesBuffer.Write(r)
- if idx < len(i.rawElements)-1 {
- bytesBuffer.WriteString(",")
- }
- }
- bytesBuffer.WriteString("]")
- indented := new(bytes.Buffer)
- if err := json.Indent(indented, bytesBuffer.Bytes(), "", " "); err != nil {
- return err
- }
- buffer = indented
- } else {
- b, err := json.MarshalIndent(i.elements, "", " ")
- if err != nil {
- return err
- }
- buffer = bytes.NewReader(b)
- }
- if _, err := io.Copy(i.outputStream, buffer); err != nil {
- return err
- }
- _, err := io.WriteString(i.outputStream, "\n")
- return err
- }
|