github.com/portworx/docker@v1.12.1/api/client/inspect/inspector.go (about)

     1  package inspect
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"text/template"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/cli"
    12  	"github.com/docker/docker/utils/templates"
    13  )
    14  
    15  // Inspector defines an interface to implement to process elements
    16  type Inspector interface {
    17  	Inspect(typedElement interface{}, rawElement []byte) error
    18  	Flush() error
    19  }
    20  
    21  // TemplateInspector uses a text template to inspect elements.
    22  type TemplateInspector struct {
    23  	outputStream io.Writer
    24  	buffer       *bytes.Buffer
    25  	tmpl         *template.Template
    26  }
    27  
    28  // NewTemplateInspector creates a new inspector with a template.
    29  func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
    30  	return &TemplateInspector{
    31  		outputStream: outputStream,
    32  		buffer:       new(bytes.Buffer),
    33  		tmpl:         tmpl,
    34  	}
    35  }
    36  
    37  // NewTemplateInspectorFromString creates a new TemplateInspector from a string
    38  // which is compiled into a template.
    39  func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, error) {
    40  	if tmplStr == "" {
    41  		return NewIndentedInspector(out), nil
    42  	}
    43  
    44  	tmpl, err := templates.Parse(tmplStr)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("Template parsing error: %s", err)
    47  	}
    48  	return NewTemplateInspector(out, tmpl), nil
    49  }
    50  
    51  // GetRefFunc is a function which used by Inspect to fetch an object from a
    52  // reference
    53  type GetRefFunc func(ref string) (interface{}, []byte, error)
    54  
    55  // Inspect fetches objects by reference using GetRefFunc and writes the json
    56  // representation to the output writer.
    57  func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFunc) error {
    58  	inspector, err := NewTemplateInspectorFromString(out, tmplStr)
    59  	if err != nil {
    60  		return cli.StatusError{StatusCode: 64, Status: err.Error()}
    61  	}
    62  
    63  	var inspectErr error
    64  	for _, ref := range references {
    65  		element, raw, err := getRef(ref)
    66  		if err != nil {
    67  			inspectErr = err
    68  			break
    69  		}
    70  
    71  		if err := inspector.Inspect(element, raw); err != nil {
    72  			inspectErr = err
    73  			break
    74  		}
    75  	}
    76  
    77  	if err := inspector.Flush(); err != nil {
    78  		logrus.Errorf("%s\n", err)
    79  	}
    80  
    81  	if inspectErr != nil {
    82  		return cli.StatusError{StatusCode: 1, Status: inspectErr.Error()}
    83  	}
    84  	return nil
    85  }
    86  
    87  // Inspect executes the inspect template.
    88  // It decodes the raw element into a map if the initial execution fails.
    89  // This allows docker cli to parse inspect structs injected with Swarm fields.
    90  func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
    91  	buffer := new(bytes.Buffer)
    92  	if err := i.tmpl.Execute(buffer, typedElement); err != nil {
    93  		if rawElement == nil {
    94  			return fmt.Errorf("Template parsing error: %v", err)
    95  		}
    96  		return i.tryRawInspectFallback(rawElement)
    97  	}
    98  	i.buffer.Write(buffer.Bytes())
    99  	i.buffer.WriteByte('\n')
   100  	return nil
   101  }
   102  
   103  // tryRawInspectFallback executes the inspect template with a raw interface.
   104  // This allows docker cli to parse inspect structs injected with Swarm fields.
   105  func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
   106  	var raw interface{}
   107  	buffer := new(bytes.Buffer)
   108  	rdr := bytes.NewReader(rawElement)
   109  	dec := json.NewDecoder(rdr)
   110  
   111  	if rawErr := dec.Decode(&raw); rawErr != nil {
   112  		return fmt.Errorf("unable to read inspect data: %v", rawErr)
   113  	}
   114  
   115  	tmplMissingKey := i.tmpl.Option("missingkey=error")
   116  	if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
   117  		return fmt.Errorf("Template parsing error: %v", rawErr)
   118  	}
   119  
   120  	i.buffer.Write(buffer.Bytes())
   121  	i.buffer.WriteByte('\n')
   122  	return nil
   123  }
   124  
   125  // Flush write the result of inspecting all elements into the output stream.
   126  func (i *TemplateInspector) Flush() error {
   127  	if i.buffer.Len() == 0 {
   128  		_, err := io.WriteString(i.outputStream, "\n")
   129  		return err
   130  	}
   131  	_, err := io.Copy(i.outputStream, i.buffer)
   132  	return err
   133  }
   134  
   135  // IndentedInspector uses a buffer to stop the indented representation of an element.
   136  type IndentedInspector struct {
   137  	outputStream io.Writer
   138  	elements     []interface{}
   139  	rawElements  [][]byte
   140  }
   141  
   142  // NewIndentedInspector generates a new IndentedInspector.
   143  func NewIndentedInspector(outputStream io.Writer) Inspector {
   144  	return &IndentedInspector{
   145  		outputStream: outputStream,
   146  	}
   147  }
   148  
   149  // Inspect writes the raw element with an indented json format.
   150  func (i *IndentedInspector) Inspect(typedElement interface{}, rawElement []byte) error {
   151  	if rawElement != nil {
   152  		i.rawElements = append(i.rawElements, rawElement)
   153  	} else {
   154  		i.elements = append(i.elements, typedElement)
   155  	}
   156  	return nil
   157  }
   158  
   159  // Flush write the result of inspecting all elements into the output stream.
   160  func (i *IndentedInspector) Flush() error {
   161  	if len(i.elements) == 0 && len(i.rawElements) == 0 {
   162  		_, err := io.WriteString(i.outputStream, "[]\n")
   163  		return err
   164  	}
   165  
   166  	var buffer io.Reader
   167  	if len(i.rawElements) > 0 {
   168  		bytesBuffer := new(bytes.Buffer)
   169  		bytesBuffer.WriteString("[")
   170  		for idx, r := range i.rawElements {
   171  			bytesBuffer.Write(r)
   172  			if idx < len(i.rawElements)-1 {
   173  				bytesBuffer.WriteString(",")
   174  			}
   175  		}
   176  		bytesBuffer.WriteString("]")
   177  		indented := new(bytes.Buffer)
   178  		if err := json.Indent(indented, bytesBuffer.Bytes(), "", "    "); err != nil {
   179  			return err
   180  		}
   181  		buffer = indented
   182  	} else {
   183  		b, err := json.MarshalIndent(i.elements, "", "    ")
   184  		if err != nil {
   185  			return err
   186  		}
   187  		buffer = bytes.NewReader(b)
   188  	}
   189  
   190  	if _, err := io.Copy(i.outputStream, buffer); err != nil {
   191  		return err
   192  	}
   193  	_, err := io.WriteString(i.outputStream, "\n")
   194  	return err
   195  }