github.com/kunnos/engine@v1.13.1/cli/command/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 writes 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 writes 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 }