Initial commit

This commit is contained in:
Ria Bhatia
2017-12-04 13:32:57 -06:00
committed by Erik St. Martin
commit 0075e5b0f3
9056 changed files with 2523100 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
package inspect
import (
"bytes"
"encoding/json"
"fmt"
"io"
"text/template"
)
// 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,
}
}
// 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, err)
}
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
}

View File

@@ -0,0 +1,40 @@
// +build !go1.5
package inspect
import (
"bytes"
"encoding/json"
"fmt"
"strings"
)
// tryeRawInspectFallback executes the inspect template with a raw interface.
// This allows docker cli to parse inspect structs injected with Swarm fields.
// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
// It doesn't allow to modify this behavior either, sending <no value> messages to the output.
// We assume that the template is invalid when there is a <no value>, if the template was valid
// we'd get <nil> or "" values. In that case we fail with the original error raised executing the
// template with the typed input.
func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte, originalErr error) 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)
}
if rawErr := i.tmpl.Execute(buffer, raw); rawErr != nil {
return fmt.Errorf("Template parsing error: %v", rawErr)
}
if strings.Contains(buffer.String(), "<no value>") {
return fmt.Errorf("Template parsing error: %v", originalErr)
}
i.buffer.Write(buffer.Bytes())
i.buffer.WriteByte('\n')
return nil
}

View File

@@ -0,0 +1,29 @@
// +build go1.5
package inspect
import (
"bytes"
"encoding/json"
"fmt"
)
func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte, _ error) 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
}

View File

@@ -0,0 +1,220 @@
package inspect
import (
"bytes"
"strings"
"testing"
"text/template"
)
type testElement struct {
DNS string `json:"Dns"`
}
func TestTemplateInspectorDefault(t *testing.T) {
b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.DNS}}")
if err != nil {
t.Fatal(err)
}
i := NewTemplateInspector(b, tmpl)
if err := i.Inspect(testElement{"0.0.0.0"}, nil); err != nil {
t.Fatal(err)
}
if err := i.Flush(); err != nil {
t.Fatal(err)
}
if b.String() != "0.0.0.0\n" {
t.Fatalf("Expected `0.0.0.0\\n`, got `%s`", b.String())
}
}
func TestTemplateInspectorEmpty(t *testing.T) {
b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.DNS}}")
if err != nil {
t.Fatal(err)
}
i := NewTemplateInspector(b, tmpl)
if err := i.Flush(); err != nil {
t.Fatal(err)
}
if b.String() != "\n" {
t.Fatalf("Expected `\\n`, got `%s`", b.String())
}
}
func TestTemplateInspectorTemplateError(t *testing.T) {
b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.Foo}}")
if err != nil {
t.Fatal(err)
}
i := NewTemplateInspector(b, tmpl)
err = i.Inspect(testElement{"0.0.0.0"}, nil)
if err == nil {
t.Fatal("Expected error got nil")
}
if !strings.HasPrefix(err.Error(), "Template parsing error") {
t.Fatalf("Expected template error, got %v", err)
}
}
func TestTemplateInspectorRawFallback(t *testing.T) {
b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.Dns}}")
if err != nil {
t.Fatal(err)
}
i := NewTemplateInspector(b, tmpl)
if err := i.Inspect(testElement{"0.0.0.0"}, []byte(`{"Dns": "0.0.0.0"}`)); err != nil {
t.Fatal(err)
}
if err := i.Flush(); err != nil {
t.Fatal(err)
}
if b.String() != "0.0.0.0\n" {
t.Fatalf("Expected `0.0.0.0\\n`, got `%s`", b.String())
}
}
func TestTemplateInspectorRawFallbackError(t *testing.T) {
b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.Dns}}")
if err != nil {
t.Fatal(err)
}
i := NewTemplateInspector(b, tmpl)
err = i.Inspect(testElement{"0.0.0.0"}, []byte(`{"Foo": "0.0.0.0"}`))
if err == nil {
t.Fatal("Expected error got nil")
}
if !strings.HasPrefix(err.Error(), "Template parsing error") {
t.Fatalf("Expected template error, got %v", err)
}
}
func TestTemplateInspectorMultiple(t *testing.T) {
b := new(bytes.Buffer)
tmpl, err := template.New("test").Parse("{{.DNS}}")
if err != nil {
t.Fatal(err)
}
i := NewTemplateInspector(b, tmpl)
if err := i.Inspect(testElement{"0.0.0.0"}, nil); err != nil {
t.Fatal(err)
}
if err := i.Inspect(testElement{"1.1.1.1"}, nil); err != nil {
t.Fatal(err)
}
if err := i.Flush(); err != nil {
t.Fatal(err)
}
if b.String() != "0.0.0.0\n1.1.1.1\n" {
t.Fatalf("Expected `0.0.0.0\\n1.1.1.1\\n`, got `%s`", b.String())
}
}
func TestIndentedInspectorDefault(t *testing.T) {
b := new(bytes.Buffer)
i := NewIndentedInspector(b)
if err := i.Inspect(testElement{"0.0.0.0"}, nil); err != nil {
t.Fatal(err)
}
if err := i.Flush(); err != nil {
t.Fatal(err)
}
expected := `[
{
"Dns": "0.0.0.0"
}
]
`
if b.String() != expected {
t.Fatalf("Expected `%s`, got `%s`", expected, b.String())
}
}
func TestIndentedInspectorMultiple(t *testing.T) {
b := new(bytes.Buffer)
i := NewIndentedInspector(b)
if err := i.Inspect(testElement{"0.0.0.0"}, nil); err != nil {
t.Fatal(err)
}
if err := i.Inspect(testElement{"1.1.1.1"}, nil); err != nil {
t.Fatal(err)
}
if err := i.Flush(); err != nil {
t.Fatal(err)
}
expected := `[
{
"Dns": "0.0.0.0"
},
{
"Dns": "1.1.1.1"
}
]
`
if b.String() != expected {
t.Fatalf("Expected `%s`, got `%s`", expected, b.String())
}
}
func TestIndentedInspectorEmpty(t *testing.T) {
b := new(bytes.Buffer)
i := NewIndentedInspector(b)
if err := i.Flush(); err != nil {
t.Fatal(err)
}
expected := "[]\n"
if b.String() != expected {
t.Fatalf("Expected `%s`, got `%s`", expected, b.String())
}
}
func TestIndentedInspectorRawElements(t *testing.T) {
b := new(bytes.Buffer)
i := NewIndentedInspector(b)
if err := i.Inspect(testElement{"0.0.0.0"}, []byte(`{"Dns": "0.0.0.0", "Node": "0"}`)); err != nil {
t.Fatal(err)
}
if err := i.Inspect(testElement{"1.1.1.1"}, []byte(`{"Dns": "1.1.1.1", "Node": "1"}`)); err != nil {
t.Fatal(err)
}
if err := i.Flush(); err != nil {
t.Fatal(err)
}
expected := `[
{
"Dns": "0.0.0.0",
"Node": "0"
},
{
"Dns": "1.1.1.1",
"Node": "1"
}
]
`
if b.String() != expected {
t.Fatalf("Expected `%s`, got `%s`", expected, b.String())
}
}