related to #10959
Signed-off-by: bobby abbott <ttobbaybbob@gmail.com>
| ... | ... |
@@ -22,9 +22,11 @@ import ( |
| 22 | 22 |
"github.com/docker/docker/graph" |
| 23 | 23 |
"github.com/docker/docker/pkg/archive" |
| 24 | 24 |
"github.com/docker/docker/pkg/fileutils" |
| 25 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 25 | 26 |
flag "github.com/docker/docker/pkg/mflag" |
| 26 | 27 |
"github.com/docker/docker/pkg/parsers" |
| 27 | 28 |
"github.com/docker/docker/pkg/progressreader" |
| 29 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 28 | 30 |
"github.com/docker/docker/pkg/symlink" |
| 29 | 31 |
"github.com/docker/docker/pkg/units" |
| 30 | 32 |
"github.com/docker/docker/pkg/urlutil" |
| ... | ... |
@@ -198,7 +200,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 198 | 198 |
// Setup an upload progress bar |
| 199 | 199 |
// FIXME: ProgressReader shouldn't be this annoying to use |
| 200 | 200 |
if context != nil {
|
| 201 |
- sf := utils.NewStreamFormatter(false) |
|
| 201 |
+ sf := streamformatter.NewStreamFormatter(false) |
|
| 202 | 202 |
body = progressreader.New(progressreader.Config{
|
| 203 | 203 |
In: context, |
| 204 | 204 |
Out: cli.out, |
| ... | ... |
@@ -291,7 +293,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 291 | 291 |
headers.Set("Content-Type", "application/tar")
|
| 292 | 292 |
} |
| 293 | 293 |
err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
|
| 294 |
- if jerr, ok := err.(*utils.JSONError); ok {
|
|
| 294 |
+ if jerr, ok := err.(*jsonmessage.JSONError); ok {
|
|
| 295 | 295 |
// If no error code is set, default to 1 |
| 296 | 296 |
if jerr.Code == 0 {
|
| 297 | 297 |
jerr.Code = 1 |
| ... | ... |
@@ -19,11 +19,11 @@ import ( |
| 19 | 19 |
"github.com/docker/docker/api" |
| 20 | 20 |
"github.com/docker/docker/autogen/dockerversion" |
| 21 | 21 |
"github.com/docker/docker/engine" |
| 22 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 22 | 23 |
"github.com/docker/docker/pkg/signal" |
| 23 | 24 |
"github.com/docker/docker/pkg/stdcopy" |
| 24 | 25 |
"github.com/docker/docker/pkg/term" |
| 25 | 26 |
"github.com/docker/docker/registry" |
| 26 |
- "github.com/docker/docker/utils" |
|
| 27 | 27 |
) |
| 28 | 28 |
|
| 29 | 29 |
var ( |
| ... | ... |
@@ -164,7 +164,7 @@ func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in |
| 164 | 164 |
} |
| 165 | 165 |
|
| 166 | 166 |
if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
| 167 |
- return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut) |
|
| 167 |
+ return jsonmessage.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut) |
|
| 168 | 168 |
} |
| 169 | 169 |
if stdout != nil || stderr != nil {
|
| 170 | 170 |
// When TTY is ON, use regular copy |
| ... | ... |
@@ -32,6 +32,7 @@ import ( |
| 32 | 32 |
"github.com/docker/docker/pkg/listenbuffer" |
| 33 | 33 |
"github.com/docker/docker/pkg/parsers" |
| 34 | 34 |
"github.com/docker/docker/pkg/stdcopy" |
| 35 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 35 | 36 |
"github.com/docker/docker/pkg/version" |
| 36 | 37 |
"github.com/docker/docker/registry" |
| 37 | 38 |
"github.com/docker/docker/utils" |
| ... | ... |
@@ -595,7 +596,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon |
| 595 | 595 |
if !job.Stdout.Used() {
|
| 596 | 596 |
return err |
| 597 | 597 |
} |
| 598 |
- sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
|
| 598 |
+ sf := streamformatter.NewStreamFormatter(version.GreaterThan("1.0"))
|
|
| 599 | 599 |
w.Write(sf.FormatError(err)) |
| 600 | 600 |
} |
| 601 | 601 |
|
| ... | ... |
@@ -680,7 +681,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response |
| 680 | 680 |
if !job.Stdout.Used() {
|
| 681 | 681 |
return err |
| 682 | 682 |
} |
| 683 |
- sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
|
| 683 |
+ sf := streamformatter.NewStreamFormatter(version.GreaterThan("1.0"))
|
|
| 684 | 684 |
w.Write(sf.FormatError(err)) |
| 685 | 685 |
} |
| 686 | 686 |
return nil |
| ... | ... |
@@ -1107,7 +1108,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite |
| 1107 | 1107 |
if !job.Stdout.Used() {
|
| 1108 | 1108 |
return err |
| 1109 | 1109 |
} |
| 1110 |
- sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
|
|
| 1110 |
+ sf := streamformatter.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
|
|
| 1111 | 1111 |
w.Write(sf.FormatError(err)) |
| 1112 | 1112 |
} |
| 1113 | 1113 |
return nil |
| ... | ... |
@@ -33,6 +33,7 @@ import ( |
| 33 | 33 |
"github.com/docker/docker/daemon" |
| 34 | 34 |
"github.com/docker/docker/engine" |
| 35 | 35 |
"github.com/docker/docker/pkg/fileutils" |
| 36 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 36 | 37 |
"github.com/docker/docker/pkg/stringid" |
| 37 | 38 |
"github.com/docker/docker/pkg/symlink" |
| 38 | 39 |
"github.com/docker/docker/pkg/tarsum" |
| ... | ... |
@@ -105,7 +106,7 @@ type Builder struct {
|
| 105 | 105 |
|
| 106 | 106 |
// Deprecated, original writer used for ImagePull. To be removed. |
| 107 | 107 |
OutOld io.Writer |
| 108 |
- StreamFormatter *utils.StreamFormatter |
|
| 108 |
+ StreamFormatter *streamformatter.StreamFormatter |
|
| 109 | 109 |
|
| 110 | 110 |
Config *runconfig.Config // runconfig for cmd, run, entrypoint etc. |
| 111 | 111 |
|
| ... | ... |
@@ -26,6 +26,7 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/pkg/archive" |
| 27 | 27 |
"github.com/docker/docker/pkg/chrootarchive" |
| 28 | 28 |
"github.com/docker/docker/pkg/ioutils" |
| 29 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 29 | 30 |
"github.com/docker/docker/pkg/parsers" |
| 30 | 31 |
"github.com/docker/docker/pkg/progressreader" |
| 31 | 32 |
"github.com/docker/docker/pkg/stringid" |
| ... | ... |
@@ -601,7 +602,7 @@ func (b *Builder) run(c *daemon.Container) error {
|
| 601 | 601 |
|
| 602 | 602 |
// Wait for it to finish |
| 603 | 603 |
if ret, _ := c.WaitStop(-1 * time.Second); ret != 0 {
|
| 604 |
- err := &utils.JSONError{
|
|
| 604 |
+ err := &jsonmessage.JSONError{
|
|
| 605 | 605 |
Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.Config.Cmd, ret),
|
| 606 | 606 |
Code: ret, |
| 607 | 607 |
} |
| ... | ... |
@@ -17,6 +17,7 @@ import ( |
| 17 | 17 |
"github.com/docker/docker/graph" |
| 18 | 18 |
"github.com/docker/docker/pkg/archive" |
| 19 | 19 |
"github.com/docker/docker/pkg/parsers" |
| 20 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 20 | 21 |
"github.com/docker/docker/pkg/urlutil" |
| 21 | 22 |
"github.com/docker/docker/registry" |
| 22 | 23 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -127,16 +128,16 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
|
| 127 | 127 |
} |
| 128 | 128 |
defer context.Close() |
| 129 | 129 |
|
| 130 |
- sf := utils.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 130 |
+ sf := streamformatter.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 131 | 131 |
|
| 132 | 132 |
builder := &Builder{
|
| 133 | 133 |
Daemon: b.Daemon, |
| 134 | 134 |
Engine: b.Engine, |
| 135 |
- OutStream: &utils.StdoutFormater{
|
|
| 135 |
+ OutStream: &streamformatter.StdoutFormater{
|
|
| 136 | 136 |
Writer: job.Stdout, |
| 137 | 137 |
StreamFormatter: sf, |
| 138 | 138 |
}, |
| 139 |
- ErrStream: &utils.StderrFormater{
|
|
| 139 |
+ ErrStream: &streamformatter.StderrFormater{
|
|
| 140 | 140 |
Writer: job.Stdout, |
| 141 | 141 |
StreamFormatter: sf, |
| 142 | 142 |
}, |
| ... | ... |
@@ -10,23 +10,23 @@ import ( |
| 10 | 10 |
"time" |
| 11 | 11 |
|
| 12 | 12 |
"github.com/docker/docker/engine" |
| 13 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 13 | 14 |
"github.com/docker/docker/pkg/parsers/filters" |
| 14 |
- "github.com/docker/docker/utils" |
|
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 | 17 |
const eventsLimit = 64 |
| 18 | 18 |
|
| 19 |
-type listener chan<- *utils.JSONMessage |
|
| 19 |
+type listener chan<- *jsonmessage.JSONMessage |
|
| 20 | 20 |
|
| 21 | 21 |
type Events struct {
|
| 22 | 22 |
mu sync.RWMutex |
| 23 |
- events []*utils.JSONMessage |
|
| 23 |
+ events []*jsonmessage.JSONMessage |
|
| 24 | 24 |
subscribers []listener |
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 | 27 |
func New() *Events {
|
| 28 | 28 |
return &Events{
|
| 29 |
- events: make([]*utils.JSONMessage, 0, eventsLimit), |
|
| 29 |
+ events: make([]*jsonmessage.JSONMessage, 0, eventsLimit), |
|
| 30 | 30 |
} |
| 31 | 31 |
} |
| 32 | 32 |
|
| ... | ... |
@@ -63,7 +63,7 @@ func (e *Events) Get(job *engine.Job) error {
|
| 63 | 63 |
timeout.Stop() |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
- listener := make(chan *utils.JSONMessage) |
|
| 66 |
+ listener := make(chan *jsonmessage.JSONMessage) |
|
| 67 | 67 |
e.subscribe(listener) |
| 68 | 68 |
defer e.unsubscribe(listener) |
| 69 | 69 |
|
| ... | ... |
@@ -107,7 +107,7 @@ func (e *Events) SubscribersCount(job *engine.Job) error {
|
| 107 | 107 |
return nil |
| 108 | 108 |
} |
| 109 | 109 |
|
| 110 |
-func writeEvent(job *engine.Job, event *utils.JSONMessage, eventFilters filters.Args) error {
|
|
| 110 |
+func writeEvent(job *engine.Job, event *jsonmessage.JSONMessage, eventFilters filters.Args) error {
|
|
| 111 | 111 |
isFiltered := func(field string, filter []string) bool {
|
| 112 | 112 |
if len(filter) == 0 {
|
| 113 | 113 |
return false |
| ... | ... |
@@ -170,7 +170,7 @@ func (e *Events) subscribersCount() int {
|
| 170 | 170 |
func (e *Events) log(action, id, from string) {
|
| 171 | 171 |
e.mu.Lock() |
| 172 | 172 |
now := time.Now().UTC().Unix() |
| 173 |
- jm := &utils.JSONMessage{Status: action, ID: id, From: from, Time: now}
|
|
| 173 |
+ jm := &jsonmessage.JSONMessage{Status: action, ID: id, From: from, Time: now}
|
|
| 174 | 174 |
if len(e.events) == cap(e.events) {
|
| 175 | 175 |
// discard oldest event |
| 176 | 176 |
copy(e.events, e.events[1:]) |
| ... | ... |
@@ -9,13 +9,13 @@ import ( |
| 9 | 9 |
"time" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/docker/engine" |
| 12 |
- "github.com/docker/docker/utils" |
|
| 12 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 13 | 13 |
) |
| 14 | 14 |
|
| 15 | 15 |
func TestEventsPublish(t *testing.T) {
|
| 16 | 16 |
e := New() |
| 17 |
- l1 := make(chan *utils.JSONMessage) |
|
| 18 |
- l2 := make(chan *utils.JSONMessage) |
|
| 17 |
+ l1 := make(chan *jsonmessage.JSONMessage) |
|
| 18 |
+ l2 := make(chan *jsonmessage.JSONMessage) |
|
| 19 | 19 |
e.subscribe(l1) |
| 20 | 20 |
e.subscribe(l2) |
| 21 | 21 |
count := e.subscribersCount() |
| ... | ... |
@@ -61,7 +61,7 @@ func TestEventsPublish(t *testing.T) {
|
| 61 | 61 |
|
| 62 | 62 |
func TestEventsPublishTimeout(t *testing.T) {
|
| 63 | 63 |
e := New() |
| 64 |
- l := make(chan *utils.JSONMessage) |
|
| 64 |
+ l := make(chan *jsonmessage.JSONMessage) |
|
| 65 | 65 |
e.subscribe(l) |
| 66 | 66 |
|
| 67 | 67 |
c := make(chan struct{})
|
| ... | ... |
@@ -108,9 +108,9 @@ func TestLogEvents(t *testing.T) {
|
| 108 | 108 |
} |
| 109 | 109 |
buf = bytes.NewBuffer(buf.Bytes()) |
| 110 | 110 |
dec := json.NewDecoder(buf) |
| 111 |
- var msgs []utils.JSONMessage |
|
| 111 |
+ var msgs []jsonmessage.JSONMessage |
|
| 112 | 112 |
for {
|
| 113 |
- var jm utils.JSONMessage |
|
| 113 |
+ var jm jsonmessage.JSONMessage |
|
| 114 | 114 |
if err := dec.Decode(&jm); err != nil {
|
| 115 | 115 |
if err == io.EOF {
|
| 116 | 116 |
break |
| ... | ... |
@@ -138,8 +138,8 @@ func TestEventsCountJob(t *testing.T) {
|
| 138 | 138 |
if err := e.Install(eng); err != nil {
|
| 139 | 139 |
t.Fatal(err) |
| 140 | 140 |
} |
| 141 |
- l1 := make(chan *utils.JSONMessage) |
|
| 142 |
- l2 := make(chan *utils.JSONMessage) |
|
| 141 |
+ l1 := make(chan *jsonmessage.JSONMessage) |
|
| 142 |
+ l2 := make(chan *jsonmessage.JSONMessage) |
|
| 143 | 143 |
e.subscribe(l1) |
| 144 | 144 |
e.subscribe(l2) |
| 145 | 145 |
job := eng.Job("subscribers_count")
|
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/image" |
| 19 | 19 |
"github.com/docker/docker/pkg/archive" |
| 20 | 20 |
"github.com/docker/docker/pkg/progressreader" |
| 21 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 21 | 22 |
"github.com/docker/docker/pkg/stringid" |
| 22 | 23 |
"github.com/docker/docker/pkg/truncindex" |
| 23 | 24 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -198,7 +199,7 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) |
| 198 | 198 |
// The archive is stored on disk and will be automatically deleted as soon as has been read. |
| 199 | 199 |
// If output is not nil, a human-readable progress bar will be written to it. |
| 200 | 200 |
// FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives? |
| 201 |
-func (graph *Graph) TempLayerArchive(id string, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
|
|
| 201 |
+func (graph *Graph) TempLayerArchive(id string, sf *streamformatter.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
|
|
| 202 | 202 |
image, err := graph.Get(id) |
| 203 | 203 |
if err != nil {
|
| 204 | 204 |
return nil, err |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/engine" |
| 12 | 12 |
"github.com/docker/docker/pkg/archive" |
| 13 | 13 |
"github.com/docker/docker/pkg/progressreader" |
| 14 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 14 | 15 |
"github.com/docker/docker/runconfig" |
| 15 | 16 |
"github.com/docker/docker/utils" |
| 16 | 17 |
) |
| ... | ... |
@@ -23,7 +24,7 @@ func (s *TagStore) CmdImport(job *engine.Job) error {
|
| 23 | 23 |
src = job.Args[0] |
| 24 | 24 |
repo = job.Args[1] |
| 25 | 25 |
tag string |
| 26 |
- sf = utils.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 26 |
+ sf = streamformatter.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 27 | 27 |
archive archive.ArchiveReader |
| 28 | 28 |
resp *http.Response |
| 29 | 29 |
stdoutBuffer = bytes.NewBuffer(nil) |
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
"github.com/docker/docker/engine" |
| 16 | 16 |
"github.com/docker/docker/image" |
| 17 | 17 |
"github.com/docker/docker/pkg/progressreader" |
| 18 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 18 | 19 |
"github.com/docker/docker/pkg/stringid" |
| 19 | 20 |
"github.com/docker/docker/registry" |
| 20 | 21 |
"github.com/docker/docker/utils" |
| ... | ... |
@@ -28,7 +29,7 @@ func (s *TagStore) CmdPull(job *engine.Job) error {
|
| 28 | 28 |
var ( |
| 29 | 29 |
localName = job.Args[0] |
| 30 | 30 |
tag string |
| 31 |
- sf = utils.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 31 |
+ sf = streamformatter.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 32 | 32 |
authConfig = ®istry.AuthConfig{}
|
| 33 | 33 |
metaHeaders map[string][]string |
| 34 | 34 |
) |
| ... | ... |
@@ -107,7 +108,7 @@ func (s *TagStore) CmdPull(job *engine.Job) error {
|
| 107 | 107 |
return nil |
| 108 | 108 |
} |
| 109 | 109 |
|
| 110 |
-func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *registry.RepositoryInfo, askedTag string, sf *utils.StreamFormatter, parallel bool) error {
|
|
| 110 |
+func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *registry.RepositoryInfo, askedTag string, sf *streamformatter.StreamFormatter, parallel bool) error {
|
|
| 111 | 111 |
out.Write(sf.FormatStatus("", "Pulling repository %s", repoInfo.CanonicalName))
|
| 112 | 112 |
|
| 113 | 113 |
repoData, err := r.GetRepositoryData(repoInfo.RemoteName) |
| ... | ... |
@@ -265,7 +266,7 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo * |
| 265 | 265 |
return nil |
| 266 | 266 |
} |
| 267 | 267 |
|
| 268 |
-func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) (bool, error) {
|
|
| 268 |
+func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *streamformatter.StreamFormatter) (bool, error) {
|
|
| 269 | 269 |
history, err := r.GetRemoteHistory(imgID, endpoint, token) |
| 270 | 270 |
if err != nil {
|
| 271 | 271 |
return false, err |
| ... | ... |
@@ -363,7 +364,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint |
| 363 | 363 |
return layers_downloaded, nil |
| 364 | 364 |
} |
| 365 | 365 |
|
| 366 |
-func WriteStatus(requestedTag string, out io.Writer, sf *utils.StreamFormatter, layers_downloaded bool) {
|
|
| 366 |
+func WriteStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layers_downloaded bool) {
|
|
| 367 | 367 |
if layers_downloaded {
|
| 368 | 368 |
out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag))
|
| 369 | 369 |
} else {
|
| ... | ... |
@@ -382,7 +383,7 @@ type downloadInfo struct {
|
| 382 | 382 |
err chan error |
| 383 | 383 |
} |
| 384 | 384 |
|
| 385 |
-func (s *TagStore) pullV2Repository(eng *engine.Engine, r *registry.Session, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *utils.StreamFormatter, parallel bool) error {
|
|
| 385 |
+func (s *TagStore) pullV2Repository(eng *engine.Engine, r *registry.Session, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *streamformatter.StreamFormatter, parallel bool) error {
|
|
| 386 | 386 |
endpoint, err := r.V2RegistryEndpoint(repoInfo.Index) |
| 387 | 387 |
if err != nil {
|
| 388 | 388 |
if repoInfo.Index.Official {
|
| ... | ... |
@@ -428,7 +429,7 @@ func (s *TagStore) pullV2Repository(eng *engine.Engine, r *registry.Session, out |
| 428 | 428 |
return nil |
| 429 | 429 |
} |
| 430 | 430 |
|
| 431 |
-func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Writer, endpoint *registry.Endpoint, repoInfo *registry.RepositoryInfo, tag string, sf *utils.StreamFormatter, parallel bool, auth *registry.RequestAuthorization) (bool, error) {
|
|
| 431 |
+func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Writer, endpoint *registry.Endpoint, repoInfo *registry.RepositoryInfo, tag string, sf *streamformatter.StreamFormatter, parallel bool, auth *registry.RequestAuthorization) (bool, error) {
|
|
| 432 | 432 |
log.Debugf("Pulling tag from V2 registry: %q", tag)
|
| 433 | 433 |
|
| 434 | 434 |
manifestBytes, manifestDigest, err := r.GetV2ImageManifest(endpoint, repoInfo.RemoteName, tag, auth) |
| ... | ... |
@@ -17,6 +17,7 @@ import ( |
| 17 | 17 |
"github.com/docker/docker/engine" |
| 18 | 18 |
"github.com/docker/docker/image" |
| 19 | 19 |
"github.com/docker/docker/pkg/progressreader" |
| 20 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 20 | 21 |
"github.com/docker/docker/pkg/stringid" |
| 21 | 22 |
"github.com/docker/docker/registry" |
| 22 | 23 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -130,7 +131,7 @@ type imagePushData struct {
|
| 130 | 130 |
|
| 131 | 131 |
// lookupImageOnEndpoint checks the specified endpoint to see if an image exists |
| 132 | 132 |
// and if it is absent then it sends the image id to the channel to be pushed. |
| 133 |
-func lookupImageOnEndpoint(wg *sync.WaitGroup, r *registry.Session, out io.Writer, sf *utils.StreamFormatter, |
|
| 133 |
+func lookupImageOnEndpoint(wg *sync.WaitGroup, r *registry.Session, out io.Writer, sf *streamformatter.StreamFormatter, |
|
| 134 | 134 |
images chan imagePushData, imagesToPush chan string) {
|
| 135 | 135 |
defer wg.Done() |
| 136 | 136 |
for image := range images {
|
| ... | ... |
@@ -144,7 +145,7 @@ func lookupImageOnEndpoint(wg *sync.WaitGroup, r *registry.Session, out io.Write |
| 144 | 144 |
} |
| 145 | 145 |
|
| 146 | 146 |
func (s *TagStore) pushImageToEndpoint(endpoint string, out io.Writer, remoteName string, imageIDs []string, |
| 147 |
- tags map[string][]string, repo *registry.RepositoryData, sf *utils.StreamFormatter, r *registry.Session) error {
|
|
| 147 |
+ tags map[string][]string, repo *registry.RepositoryData, sf *streamformatter.StreamFormatter, r *registry.Session) error {
|
|
| 148 | 148 |
workerCount := len(imageIDs) |
| 149 | 149 |
// start a maximum of 5 workers to check if images exist on the specified endpoint. |
| 150 | 150 |
if workerCount > 5 {
|
| ... | ... |
@@ -203,7 +204,7 @@ func (s *TagStore) pushImageToEndpoint(endpoint string, out io.Writer, remoteNam |
| 203 | 203 |
// pushRepository pushes layers that do not already exist on the registry. |
| 204 | 204 |
func (s *TagStore) pushRepository(r *registry.Session, out io.Writer, |
| 205 | 205 |
repoInfo *registry.RepositoryInfo, localRepo map[string]string, |
| 206 |
- tag string, sf *utils.StreamFormatter) error {
|
|
| 206 |
+ tag string, sf *streamformatter.StreamFormatter) error {
|
|
| 207 | 207 |
log.Debugf("Local repo: %s", localRepo)
|
| 208 | 208 |
out = utils.NewWriteFlusher(out) |
| 209 | 209 |
imgList, tags, err := s.getImageList(localRepo, tag) |
| ... | ... |
@@ -238,7 +239,7 @@ func (s *TagStore) pushRepository(r *registry.Session, out io.Writer, |
| 238 | 238 |
return err |
| 239 | 239 |
} |
| 240 | 240 |
|
| 241 |
-func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep string, token []string, sf *utils.StreamFormatter) (checksum string, err error) {
|
|
| 241 |
+func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep string, token []string, sf *streamformatter.StreamFormatter) (checksum string, err error) {
|
|
| 242 | 242 |
out = utils.NewWriteFlusher(out) |
| 243 | 243 |
jsonRaw, err := ioutil.ReadFile(path.Join(s.graph.Root, imgID, "json")) |
| 244 | 244 |
if err != nil {
|
| ... | ... |
@@ -292,7 +293,7 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin |
| 292 | 292 |
return imgData.Checksum, nil |
| 293 | 293 |
} |
| 294 | 294 |
|
| 295 |
-func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *utils.StreamFormatter) error {
|
|
| 295 |
+func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *streamformatter.StreamFormatter) error {
|
|
| 296 | 296 |
endpoint, err := r.V2RegistryEndpoint(repoInfo.Index) |
| 297 | 297 |
if err != nil {
|
| 298 | 298 |
if repoInfo.Index.Official {
|
| ... | ... |
@@ -442,7 +443,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o |
| 442 | 442 |
} |
| 443 | 443 |
|
| 444 | 444 |
// PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk |
| 445 |
-func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName string, sf *utils.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (string, error) {
|
|
| 445 |
+func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (string, error) {
|
|
| 446 | 446 |
out.Write(sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil)) |
| 447 | 447 |
|
| 448 | 448 |
image, err := s.graph.Get(img.ID) |
| ... | ... |
@@ -498,7 +499,7 @@ func (s *TagStore) CmdPush(job *engine.Job) error {
|
| 498 | 498 |
} |
| 499 | 499 |
var ( |
| 500 | 500 |
localName = job.Args[0] |
| 501 |
- sf = utils.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 501 |
+ sf = streamformatter.NewStreamFormatter(job.GetenvBool("json"))
|
|
| 502 | 502 |
authConfig = ®istry.AuthConfig{}
|
| 503 | 503 |
metaHeaders map[string][]string |
| 504 | 504 |
) |
| 505 | 505 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,172 @@ |
| 0 |
+package jsonmessage |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "io" |
|
| 6 |
+ "strings" |
|
| 7 |
+ "time" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/docker/docker/pkg/term" |
|
| 10 |
+ "github.com/docker/docker/pkg/timeutils" |
|
| 11 |
+ "github.com/docker/docker/pkg/units" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+type JSONError struct {
|
|
| 15 |
+ Code int `json:"code,omitempty"` |
|
| 16 |
+ Message string `json:"message,omitempty"` |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func (e *JSONError) Error() string {
|
|
| 20 |
+ return e.Message |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+type JSONProgress struct {
|
|
| 24 |
+ terminalFd uintptr |
|
| 25 |
+ Current int `json:"current,omitempty"` |
|
| 26 |
+ Total int `json:"total,omitempty"` |
|
| 27 |
+ Start int64 `json:"start,omitempty"` |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func (p *JSONProgress) String() string {
|
|
| 31 |
+ var ( |
|
| 32 |
+ width = 200 |
|
| 33 |
+ pbBox string |
|
| 34 |
+ numbersBox string |
|
| 35 |
+ timeLeftBox string |
|
| 36 |
+ ) |
|
| 37 |
+ |
|
| 38 |
+ ws, err := term.GetWinsize(p.terminalFd) |
|
| 39 |
+ if err == nil {
|
|
| 40 |
+ width = int(ws.Width) |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ if p.Current <= 0 && p.Total <= 0 {
|
|
| 44 |
+ return "" |
|
| 45 |
+ } |
|
| 46 |
+ current := units.HumanSize(float64(p.Current)) |
|
| 47 |
+ if p.Total <= 0 {
|
|
| 48 |
+ return fmt.Sprintf("%8v", current)
|
|
| 49 |
+ } |
|
| 50 |
+ total := units.HumanSize(float64(p.Total)) |
|
| 51 |
+ percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 |
|
| 52 |
+ if percentage > 50 {
|
|
| 53 |
+ percentage = 50 |
|
| 54 |
+ } |
|
| 55 |
+ if width > 110 {
|
|
| 56 |
+ // this number can't be negetive gh#7136 |
|
| 57 |
+ numSpaces := 0 |
|
| 58 |
+ if 50-percentage > 0 {
|
|
| 59 |
+ numSpaces = 50 - percentage |
|
| 60 |
+ } |
|
| 61 |
+ pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
|
|
| 62 |
+ } |
|
| 63 |
+ numbersBox = fmt.Sprintf("%8v/%v", current, total)
|
|
| 64 |
+ |
|
| 65 |
+ if p.Current > 0 && p.Start > 0 && percentage < 50 {
|
|
| 66 |
+ fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) |
|
| 67 |
+ perEntry := fromStart / time.Duration(p.Current) |
|
| 68 |
+ left := time.Duration(p.Total-p.Current) * perEntry |
|
| 69 |
+ left = (left / time.Second) * time.Second |
|
| 70 |
+ |
|
| 71 |
+ if width > 50 {
|
|
| 72 |
+ timeLeftBox = " " + left.String() |
|
| 73 |
+ } |
|
| 74 |
+ } |
|
| 75 |
+ return pbBox + numbersBox + timeLeftBox |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+type JSONMessage struct {
|
|
| 79 |
+ Stream string `json:"stream,omitempty"` |
|
| 80 |
+ Status string `json:"status,omitempty"` |
|
| 81 |
+ Progress *JSONProgress `json:"progressDetail,omitempty"` |
|
| 82 |
+ ProgressMessage string `json:"progress,omitempty"` //deprecated |
|
| 83 |
+ ID string `json:"id,omitempty"` |
|
| 84 |
+ From string `json:"from,omitempty"` |
|
| 85 |
+ Time int64 `json:"time,omitempty"` |
|
| 86 |
+ Error *JSONError `json:"errorDetail,omitempty"` |
|
| 87 |
+ ErrorMessage string `json:"error,omitempty"` //deprecated |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
|
|
| 91 |
+ if jm.Error != nil {
|
|
| 92 |
+ if jm.Error.Code == 401 {
|
|
| 93 |
+ return fmt.Errorf("Authentication is required.")
|
|
| 94 |
+ } |
|
| 95 |
+ return jm.Error |
|
| 96 |
+ } |
|
| 97 |
+ var endl string |
|
| 98 |
+ if isTerminal && jm.Stream == "" && jm.Progress != nil {
|
|
| 99 |
+ // <ESC>[2K = erase entire current line |
|
| 100 |
+ fmt.Fprintf(out, "%c[2K\r", 27) |
|
| 101 |
+ endl = "\r" |
|
| 102 |
+ } else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
|
|
| 103 |
+ return nil |
|
| 104 |
+ } |
|
| 105 |
+ if jm.Time != 0 {
|
|
| 106 |
+ fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(timeutils.RFC3339NanoFixed)) |
|
| 107 |
+ } |
|
| 108 |
+ if jm.ID != "" {
|
|
| 109 |
+ fmt.Fprintf(out, "%s: ", jm.ID) |
|
| 110 |
+ } |
|
| 111 |
+ if jm.From != "" {
|
|
| 112 |
+ fmt.Fprintf(out, "(from %s) ", jm.From) |
|
| 113 |
+ } |
|
| 114 |
+ if jm.Progress != nil && isTerminal {
|
|
| 115 |
+ fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl) |
|
| 116 |
+ } else if jm.ProgressMessage != "" { //deprecated
|
|
| 117 |
+ fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl) |
|
| 118 |
+ } else if jm.Stream != "" {
|
|
| 119 |
+ fmt.Fprintf(out, "%s%s", jm.Stream, endl) |
|
| 120 |
+ } else {
|
|
| 121 |
+ fmt.Fprintf(out, "%s%s\n", jm.Status, endl) |
|
| 122 |
+ } |
|
| 123 |
+ return nil |
|
| 124 |
+} |
|
| 125 |
+ |
|
| 126 |
+func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool) error {
|
|
| 127 |
+ var ( |
|
| 128 |
+ dec = json.NewDecoder(in) |
|
| 129 |
+ ids = make(map[string]int) |
|
| 130 |
+ diff = 0 |
|
| 131 |
+ ) |
|
| 132 |
+ for {
|
|
| 133 |
+ var jm JSONMessage |
|
| 134 |
+ if err := dec.Decode(&jm); err != nil {
|
|
| 135 |
+ if err == io.EOF {
|
|
| 136 |
+ break |
|
| 137 |
+ } |
|
| 138 |
+ return err |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ if jm.Progress != nil {
|
|
| 142 |
+ jm.Progress.terminalFd = terminalFd |
|
| 143 |
+ } |
|
| 144 |
+ if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
|
|
| 145 |
+ line, ok := ids[jm.ID] |
|
| 146 |
+ if !ok {
|
|
| 147 |
+ line = len(ids) |
|
| 148 |
+ ids[jm.ID] = line |
|
| 149 |
+ if isTerminal {
|
|
| 150 |
+ fmt.Fprintf(out, "\n") |
|
| 151 |
+ } |
|
| 152 |
+ diff = 0 |
|
| 153 |
+ } else {
|
|
| 154 |
+ diff = len(ids) - line |
|
| 155 |
+ } |
|
| 156 |
+ if jm.ID != "" && isTerminal {
|
|
| 157 |
+ // <ESC>[{diff}A = move cursor up diff rows
|
|
| 158 |
+ fmt.Fprintf(out, "%c[%dA", 27, diff) |
|
| 159 |
+ } |
|
| 160 |
+ } |
|
| 161 |
+ err := jm.Display(out, isTerminal) |
|
| 162 |
+ if jm.ID != "" && isTerminal {
|
|
| 163 |
+ // <ESC>[{diff}B = move cursor down diff rows
|
|
| 164 |
+ fmt.Fprintf(out, "%c[%dB", 27, diff) |
|
| 165 |
+ } |
|
| 166 |
+ if err != nil {
|
|
| 167 |
+ return err |
|
| 168 |
+ } |
|
| 169 |
+ } |
|
| 170 |
+ return nil |
|
| 171 |
+} |
| 0 | 172 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,38 @@ |
| 0 |
+package jsonmessage |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func TestError(t *testing.T) {
|
|
| 7 |
+ je := JSONError{404, "Not found"}
|
|
| 8 |
+ if je.Error() != "Not found" {
|
|
| 9 |
+ t.Fatalf("Expected 'Not found' got '%s'", je.Error())
|
|
| 10 |
+ } |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func TestProgress(t *testing.T) {
|
|
| 14 |
+ jp := JSONProgress{}
|
|
| 15 |
+ if jp.String() != "" {
|
|
| 16 |
+ t.Fatalf("Expected empty string, got '%s'", jp.String())
|
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ expected := " 1 B" |
|
| 20 |
+ jp2 := JSONProgress{Current: 1}
|
|
| 21 |
+ if jp2.String() != expected {
|
|
| 22 |
+ t.Fatalf("Expected %q, got %q", expected, jp2.String())
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ expected = "[=========================> ] 50 B/100 B" |
|
| 26 |
+ jp3 := JSONProgress{Current: 50, Total: 100}
|
|
| 27 |
+ if jp3.String() != expected {
|
|
| 28 |
+ t.Fatalf("Expected %q, got %q", expected, jp3.String())
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ // this number can't be negetive gh#7136 |
|
| 32 |
+ expected = "[==================================================>] 50 B/40 B" |
|
| 33 |
+ jp4 := JSONProgress{Current: 50, Total: 40}
|
|
| 34 |
+ if jp4.String() != expected {
|
|
| 35 |
+ t.Fatalf("Expected %q, got %q", expected, jp4.String())
|
|
| 36 |
+ } |
|
| 37 |
+} |
| ... | ... |
@@ -1,37 +1,16 @@ |
| 1 | 1 |
package progressreader |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 5 |
+ "github.com/docker/docker/pkg/streamformatter" |
|
| 4 | 6 |
"io" |
| 5 | 7 |
) |
| 6 | 8 |
|
| 7 |
-type StreamFormatter interface {
|
|
| 8 |
- FormatProg(string, string, interface{}) []byte
|
|
| 9 |
- FormatStatus(string, string, ...interface{}) []byte
|
|
| 10 |
- FormatError(error) []byte |
|
| 11 |
-} |
|
| 12 |
- |
|
| 13 |
-type PR_JSONProgress interface {
|
|
| 14 |
- GetCurrent() int |
|
| 15 |
- GetTotal() int |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-type JSONProg struct {
|
|
| 19 |
- Current int |
|
| 20 |
- Total int |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func (j *JSONProg) GetCurrent() int {
|
|
| 24 |
- return j.Current |
|
| 25 |
-} |
|
| 26 |
-func (j *JSONProg) GetTotal() int {
|
|
| 27 |
- return j.Total |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 | 9 |
// Reader with progress bar |
| 31 | 10 |
type Config struct {
|
| 32 | 11 |
In io.ReadCloser // Stream to read from |
| 33 | 12 |
Out io.Writer // Where to send progress bar to |
| 34 |
- Formatter StreamFormatter |
|
| 13 |
+ Formatter *streamformatter.StreamFormatter |
|
| 35 | 14 |
Size int |
| 36 | 15 |
Current int |
| 37 | 16 |
LastUpdate int |
| ... | ... |
@@ -54,7 +33,7 @@ func (config *Config) Read(p []byte) (n int, err error) {
|
| 54 | 54 |
} |
| 55 | 55 |
} |
| 56 | 56 |
if config.Current-config.LastUpdate > updateEvery || err != nil {
|
| 57 |
- config.Out.Write(config.Formatter.FormatProg(config.ID, config.Action, &JSONProg{Current: config.Current, Total: config.Size}))
|
|
| 57 |
+ config.Out.Write(config.Formatter.FormatProgress(config.ID, config.Action, &jsonmessage.JSONProgress{Current: config.Current, Total: config.Size}))
|
|
| 58 | 58 |
config.LastUpdate = config.Current |
| 59 | 59 |
} |
| 60 | 60 |
// Send newline when complete |
| ... | ... |
@@ -64,6 +43,6 @@ func (config *Config) Read(p []byte) (n int, err error) {
|
| 64 | 64 |
return read, err |
| 65 | 65 |
} |
| 66 | 66 |
func (config *Config) Close() error {
|
| 67 |
- config.Out.Write(config.Formatter.FormatProg(config.ID, config.Action, &JSONProg{Current: config.Current, Total: config.Size}))
|
|
| 67 |
+ config.Out.Write(config.Formatter.FormatProgress(config.ID, config.Action, &jsonmessage.JSONProgress{Current: config.Current, Total: config.Size}))
|
|
| 68 | 68 |
return config.In.Close() |
| 69 | 69 |
} |
| 70 | 70 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,113 @@ |
| 0 |
+package streamformatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 6 |
+ "io" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+type StreamFormatter struct {
|
|
| 10 |
+ json bool |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func NewStreamFormatter(json bool) *StreamFormatter {
|
|
| 14 |
+ return &StreamFormatter{json}
|
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+const streamNewline = "\r\n" |
|
| 18 |
+ |
|
| 19 |
+var streamNewlineBytes = []byte(streamNewline) |
|
| 20 |
+ |
|
| 21 |
+func (sf *StreamFormatter) FormatStream(str string) []byte {
|
|
| 22 |
+ if sf.json {
|
|
| 23 |
+ b, err := json.Marshal(&jsonmessage.JSONMessage{Stream: str})
|
|
| 24 |
+ if err != nil {
|
|
| 25 |
+ return sf.FormatError(err) |
|
| 26 |
+ } |
|
| 27 |
+ return append(b, streamNewlineBytes...) |
|
| 28 |
+ } |
|
| 29 |
+ return []byte(str + "\r") |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte {
|
|
| 33 |
+ str := fmt.Sprintf(format, a...) |
|
| 34 |
+ if sf.json {
|
|
| 35 |
+ b, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Status: str})
|
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ return sf.FormatError(err) |
|
| 38 |
+ } |
|
| 39 |
+ return append(b, streamNewlineBytes...) |
|
| 40 |
+ } |
|
| 41 |
+ return []byte(str + streamNewline) |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func (sf *StreamFormatter) FormatError(err error) []byte {
|
|
| 45 |
+ if sf.json {
|
|
| 46 |
+ jsonError, ok := err.(*jsonmessage.JSONError) |
|
| 47 |
+ if !ok {
|
|
| 48 |
+ jsonError = &jsonmessage.JSONError{Message: err.Error()}
|
|
| 49 |
+ } |
|
| 50 |
+ if b, err := json.Marshal(&jsonmessage.JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil {
|
|
| 51 |
+ return append(b, streamNewlineBytes...) |
|
| 52 |
+ } |
|
| 53 |
+ return []byte("{\"error\":\"format error\"}" + streamNewline)
|
|
| 54 |
+ } |
|
| 55 |
+ return []byte("Error: " + err.Error() + streamNewline)
|
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessage.JSONProgress) []byte {
|
|
| 59 |
+ if progress == nil {
|
|
| 60 |
+ progress = &jsonmessage.JSONProgress{}
|
|
| 61 |
+ } |
|
| 62 |
+ if sf.json {
|
|
| 63 |
+ |
|
| 64 |
+ b, err := json.Marshal(&jsonmessage.JSONMessage{
|
|
| 65 |
+ Status: action, |
|
| 66 |
+ ProgressMessage: progress.String(), |
|
| 67 |
+ Progress: progress, |
|
| 68 |
+ ID: id, |
|
| 69 |
+ }) |
|
| 70 |
+ if err != nil {
|
|
| 71 |
+ return nil |
|
| 72 |
+ } |
|
| 73 |
+ return b |
|
| 74 |
+ } |
|
| 75 |
+ endl := "\r" |
|
| 76 |
+ if progress.String() == "" {
|
|
| 77 |
+ endl += "\n" |
|
| 78 |
+ } |
|
| 79 |
+ return []byte(action + " " + progress.String() + endl) |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func (sf *StreamFormatter) Json() bool {
|
|
| 83 |
+ return sf.json |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+type StdoutFormater struct {
|
|
| 87 |
+ io.Writer |
|
| 88 |
+ *StreamFormatter |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func (sf *StdoutFormater) Write(buf []byte) (int, error) {
|
|
| 92 |
+ formattedBuf := sf.StreamFormatter.FormatStream(string(buf)) |
|
| 93 |
+ n, err := sf.Writer.Write(formattedBuf) |
|
| 94 |
+ if n != len(formattedBuf) {
|
|
| 95 |
+ return n, io.ErrShortWrite |
|
| 96 |
+ } |
|
| 97 |
+ return len(buf), err |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+type StderrFormater struct {
|
|
| 101 |
+ io.Writer |
|
| 102 |
+ *StreamFormatter |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+func (sf *StderrFormater) Write(buf []byte) (int, error) {
|
|
| 106 |
+ formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m")
|
|
| 107 |
+ n, err := sf.Writer.Write(formattedBuf) |
|
| 108 |
+ if n != len(formattedBuf) {
|
|
| 109 |
+ return n, io.ErrShortWrite |
|
| 110 |
+ } |
|
| 111 |
+ return len(buf), err |
|
| 112 |
+} |
| 0 | 113 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,68 @@ |
| 0 |
+package streamformatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 6 |
+ "reflect" |
|
| 7 |
+ "testing" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func TestFormatStream(t *testing.T) {
|
|
| 11 |
+ sf := NewStreamFormatter(true) |
|
| 12 |
+ res := sf.FormatStream("stream")
|
|
| 13 |
+ if string(res) != `{"stream":"stream"}`+"\r\n" {
|
|
| 14 |
+ t.Fatalf("%q", res)
|
|
| 15 |
+ } |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func TestFormatStatus(t *testing.T) {
|
|
| 19 |
+ sf := NewStreamFormatter(true) |
|
| 20 |
+ res := sf.FormatStatus("ID", "%s%d", "a", 1)
|
|
| 21 |
+ if string(res) != `{"status":"a1","id":"ID"}`+"\r\n" {
|
|
| 22 |
+ t.Fatalf("%q", res)
|
|
| 23 |
+ } |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func TestFormatSimpleError(t *testing.T) {
|
|
| 27 |
+ sf := NewStreamFormatter(true) |
|
| 28 |
+ res := sf.FormatError(errors.New("Error for formatter"))
|
|
| 29 |
+ if string(res) != `{"errorDetail":{"message":"Error for formatter"},"error":"Error for formatter"}`+"\r\n" {
|
|
| 30 |
+ t.Fatalf("%q", res)
|
|
| 31 |
+ } |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func TestFormatJSONError(t *testing.T) {
|
|
| 35 |
+ sf := NewStreamFormatter(true) |
|
| 36 |
+ err := &jsonmessage.JSONError{Code: 50, Message: "Json error"}
|
|
| 37 |
+ res := sf.FormatError(err) |
|
| 38 |
+ if string(res) != `{"errorDetail":{"code":50,"message":"Json error"},"error":"Json error"}`+"\r\n" {
|
|
| 39 |
+ t.Fatalf("%q", res)
|
|
| 40 |
+ } |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func TestFormatProgress(t *testing.T) {
|
|
| 44 |
+ sf := NewStreamFormatter(true) |
|
| 45 |
+ progress := &jsonmessage.JSONProgress{
|
|
| 46 |
+ Current: 15, |
|
| 47 |
+ Total: 30, |
|
| 48 |
+ Start: 1, |
|
| 49 |
+ } |
|
| 50 |
+ res := sf.FormatProgress("id", "action", progress)
|
|
| 51 |
+ msg := &jsonmessage.JSONMessage{}
|
|
| 52 |
+ if err := json.Unmarshal(res, msg); err != nil {
|
|
| 53 |
+ t.Fatal(err) |
|
| 54 |
+ } |
|
| 55 |
+ if msg.ID != "id" {
|
|
| 56 |
+ t.Fatalf("ID must be 'id', got: %s", msg.ID)
|
|
| 57 |
+ } |
|
| 58 |
+ if msg.Status != "action" {
|
|
| 59 |
+ t.Fatalf("Status must be 'action', got: %s", msg.Status)
|
|
| 60 |
+ } |
|
| 61 |
+ if msg.ProgressMessage != progress.String() {
|
|
| 62 |
+ t.Fatalf("ProgressMessage must be %s, got: %s", progress.String(), msg.ProgressMessage)
|
|
| 63 |
+ } |
|
| 64 |
+ if !reflect.DeepEqual(msg.Progress, progress) {
|
|
| 65 |
+ t.Fatal("Original progress not equals progress from FormatProgress")
|
|
| 66 |
+ } |
|
| 67 |
+} |
| 0 | 68 |
deleted file mode 100644 |
| ... | ... |
@@ -1,172 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "strings" |
|
| 8 |
- "time" |
|
| 9 |
- |
|
| 10 |
- "github.com/docker/docker/pkg/term" |
|
| 11 |
- "github.com/docker/docker/pkg/timeutils" |
|
| 12 |
- "github.com/docker/docker/pkg/units" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type JSONError struct {
|
|
| 16 |
- Code int `json:"code,omitempty"` |
|
| 17 |
- Message string `json:"message,omitempty"` |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-func (e *JSONError) Error() string {
|
|
| 21 |
- return e.Message |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-type JSONProgress struct {
|
|
| 25 |
- terminalFd uintptr |
|
| 26 |
- Current int `json:"current,omitempty"` |
|
| 27 |
- Total int `json:"total,omitempty"` |
|
| 28 |
- Start int64 `json:"start,omitempty"` |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func (p *JSONProgress) String() string {
|
|
| 32 |
- var ( |
|
| 33 |
- width = 200 |
|
| 34 |
- pbBox string |
|
| 35 |
- numbersBox string |
|
| 36 |
- timeLeftBox string |
|
| 37 |
- ) |
|
| 38 |
- |
|
| 39 |
- ws, err := term.GetWinsize(p.terminalFd) |
|
| 40 |
- if err == nil {
|
|
| 41 |
- width = int(ws.Width) |
|
| 42 |
- } |
|
| 43 |
- |
|
| 44 |
- if p.Current <= 0 && p.Total <= 0 {
|
|
| 45 |
- return "" |
|
| 46 |
- } |
|
| 47 |
- current := units.HumanSize(float64(p.Current)) |
|
| 48 |
- if p.Total <= 0 {
|
|
| 49 |
- return fmt.Sprintf("%8v", current)
|
|
| 50 |
- } |
|
| 51 |
- total := units.HumanSize(float64(p.Total)) |
|
| 52 |
- percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 |
|
| 53 |
- if percentage > 50 {
|
|
| 54 |
- percentage = 50 |
|
| 55 |
- } |
|
| 56 |
- if width > 110 {
|
|
| 57 |
- // this number can't be negetive gh#7136 |
|
| 58 |
- numSpaces := 0 |
|
| 59 |
- if 50-percentage > 0 {
|
|
| 60 |
- numSpaces = 50 - percentage |
|
| 61 |
- } |
|
| 62 |
- pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
|
|
| 63 |
- } |
|
| 64 |
- numbersBox = fmt.Sprintf("%8v/%v", current, total)
|
|
| 65 |
- |
|
| 66 |
- if p.Current > 0 && p.Start > 0 && percentage < 50 {
|
|
| 67 |
- fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) |
|
| 68 |
- perEntry := fromStart / time.Duration(p.Current) |
|
| 69 |
- left := time.Duration(p.Total-p.Current) * perEntry |
|
| 70 |
- left = (left / time.Second) * time.Second |
|
| 71 |
- |
|
| 72 |
- if width > 50 {
|
|
| 73 |
- timeLeftBox = " " + left.String() |
|
| 74 |
- } |
|
| 75 |
- } |
|
| 76 |
- return pbBox + numbersBox + timeLeftBox |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-type JSONMessage struct {
|
|
| 80 |
- Stream string `json:"stream,omitempty"` |
|
| 81 |
- Status string `json:"status,omitempty"` |
|
| 82 |
- Progress *JSONProgress `json:"progressDetail,omitempty"` |
|
| 83 |
- ProgressMessage string `json:"progress,omitempty"` //deprecated |
|
| 84 |
- ID string `json:"id,omitempty"` |
|
| 85 |
- From string `json:"from,omitempty"` |
|
| 86 |
- Time int64 `json:"time,omitempty"` |
|
| 87 |
- Error *JSONError `json:"errorDetail,omitempty"` |
|
| 88 |
- ErrorMessage string `json:"error,omitempty"` //deprecated |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
|
|
| 92 |
- if jm.Error != nil {
|
|
| 93 |
- if jm.Error.Code == 401 {
|
|
| 94 |
- return fmt.Errorf("Authentication is required.")
|
|
| 95 |
- } |
|
| 96 |
- return jm.Error |
|
| 97 |
- } |
|
| 98 |
- var endl string |
|
| 99 |
- if isTerminal && jm.Stream == "" && jm.Progress != nil {
|
|
| 100 |
- // <ESC>[2K = erase entire current line |
|
| 101 |
- fmt.Fprintf(out, "%c[2K\r", 27) |
|
| 102 |
- endl = "\r" |
|
| 103 |
- } else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
|
|
| 104 |
- return nil |
|
| 105 |
- } |
|
| 106 |
- if jm.Time != 0 {
|
|
| 107 |
- fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(timeutils.RFC3339NanoFixed)) |
|
| 108 |
- } |
|
| 109 |
- if jm.ID != "" {
|
|
| 110 |
- fmt.Fprintf(out, "%s: ", jm.ID) |
|
| 111 |
- } |
|
| 112 |
- if jm.From != "" {
|
|
| 113 |
- fmt.Fprintf(out, "(from %s) ", jm.From) |
|
| 114 |
- } |
|
| 115 |
- if jm.Progress != nil && isTerminal {
|
|
| 116 |
- fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl) |
|
| 117 |
- } else if jm.ProgressMessage != "" { //deprecated
|
|
| 118 |
- fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl) |
|
| 119 |
- } else if jm.Stream != "" {
|
|
| 120 |
- fmt.Fprintf(out, "%s%s", jm.Stream, endl) |
|
| 121 |
- } else {
|
|
| 122 |
- fmt.Fprintf(out, "%s%s\n", jm.Status, endl) |
|
| 123 |
- } |
|
| 124 |
- return nil |
|
| 125 |
-} |
|
| 126 |
- |
|
| 127 |
-func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool) error {
|
|
| 128 |
- var ( |
|
| 129 |
- dec = json.NewDecoder(in) |
|
| 130 |
- ids = make(map[string]int) |
|
| 131 |
- diff = 0 |
|
| 132 |
- ) |
|
| 133 |
- for {
|
|
| 134 |
- var jm JSONMessage |
|
| 135 |
- if err := dec.Decode(&jm); err != nil {
|
|
| 136 |
- if err == io.EOF {
|
|
| 137 |
- break |
|
| 138 |
- } |
|
| 139 |
- return err |
|
| 140 |
- } |
|
| 141 |
- |
|
| 142 |
- if jm.Progress != nil {
|
|
| 143 |
- jm.Progress.terminalFd = terminalFd |
|
| 144 |
- } |
|
| 145 |
- if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
|
|
| 146 |
- line, ok := ids[jm.ID] |
|
| 147 |
- if !ok {
|
|
| 148 |
- line = len(ids) |
|
| 149 |
- ids[jm.ID] = line |
|
| 150 |
- if isTerminal {
|
|
| 151 |
- fmt.Fprintf(out, "\n") |
|
| 152 |
- } |
|
| 153 |
- diff = 0 |
|
| 154 |
- } else {
|
|
| 155 |
- diff = len(ids) - line |
|
| 156 |
- } |
|
| 157 |
- if jm.ID != "" && isTerminal {
|
|
| 158 |
- // <ESC>[{diff}A = move cursor up diff rows
|
|
| 159 |
- fmt.Fprintf(out, "%c[%dA", 27, diff) |
|
| 160 |
- } |
|
| 161 |
- } |
|
| 162 |
- err := jm.Display(out, isTerminal) |
|
| 163 |
- if jm.ID != "" && isTerminal {
|
|
| 164 |
- // <ESC>[{diff}B = move cursor down diff rows
|
|
| 165 |
- fmt.Fprintf(out, "%c[%dB", 27, diff) |
|
| 166 |
- } |
|
| 167 |
- if err != nil {
|
|
| 168 |
- return err |
|
| 169 |
- } |
|
| 170 |
- } |
|
| 171 |
- return nil |
|
| 172 |
-} |
| 173 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,38 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-func TestError(t *testing.T) {
|
|
| 8 |
- je := JSONError{404, "Not found"}
|
|
| 9 |
- if je.Error() != "Not found" {
|
|
| 10 |
- t.Fatalf("Expected 'Not found' got '%s'", je.Error())
|
|
| 11 |
- } |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func TestProgress(t *testing.T) {
|
|
| 15 |
- jp := JSONProgress{}
|
|
| 16 |
- if jp.String() != "" {
|
|
| 17 |
- t.Fatalf("Expected empty string, got '%s'", jp.String())
|
|
| 18 |
- } |
|
| 19 |
- |
|
| 20 |
- expected := " 1 B" |
|
| 21 |
- jp2 := JSONProgress{Current: 1}
|
|
| 22 |
- if jp2.String() != expected {
|
|
| 23 |
- t.Fatalf("Expected %q, got %q", expected, jp2.String())
|
|
| 24 |
- } |
|
| 25 |
- |
|
| 26 |
- expected = "[=========================> ] 50 B/100 B" |
|
| 27 |
- jp3 := JSONProgress{Current: 50, Total: 100}
|
|
| 28 |
- if jp3.String() != expected {
|
|
| 29 |
- t.Fatalf("Expected %q, got %q", expected, jp3.String())
|
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- // this number can't be negetive gh#7136 |
|
| 33 |
- expected = "[==================================================>] 50 B/40 B" |
|
| 34 |
- jp4 := JSONProgress{Current: 50, Total: 40}
|
|
| 35 |
- if jp4.String() != expected {
|
|
| 36 |
- t.Fatalf("Expected %q, got %q", expected, jp4.String())
|
|
| 37 |
- } |
|
| 38 |
-} |
| 39 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,121 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/docker/docker/pkg/progressreader" |
|
| 7 |
- "io" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-type StreamFormatter struct {
|
|
| 11 |
- json bool |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func NewStreamFormatter(json bool) *StreamFormatter {
|
|
| 15 |
- return &StreamFormatter{json}
|
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-const streamNewline = "\r\n" |
|
| 19 |
- |
|
| 20 |
-var streamNewlineBytes = []byte(streamNewline) |
|
| 21 |
- |
|
| 22 |
-func (sf *StreamFormatter) FormatStream(str string) []byte {
|
|
| 23 |
- if sf.json {
|
|
| 24 |
- b, err := json.Marshal(&JSONMessage{Stream: str})
|
|
| 25 |
- if err != nil {
|
|
| 26 |
- return sf.FormatError(err) |
|
| 27 |
- } |
|
| 28 |
- return append(b, streamNewlineBytes...) |
|
| 29 |
- } |
|
| 30 |
- return []byte(str + "\r") |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte {
|
|
| 34 |
- str := fmt.Sprintf(format, a...) |
|
| 35 |
- if sf.json {
|
|
| 36 |
- b, err := json.Marshal(&JSONMessage{ID: id, Status: str})
|
|
| 37 |
- if err != nil {
|
|
| 38 |
- return sf.FormatError(err) |
|
| 39 |
- } |
|
| 40 |
- return append(b, streamNewlineBytes...) |
|
| 41 |
- } |
|
| 42 |
- return []byte(str + streamNewline) |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-func (sf *StreamFormatter) FormatError(err error) []byte {
|
|
| 46 |
- if sf.json {
|
|
| 47 |
- jsonError, ok := err.(*JSONError) |
|
| 48 |
- if !ok {
|
|
| 49 |
- jsonError = &JSONError{Message: err.Error()}
|
|
| 50 |
- } |
|
| 51 |
- if b, err := json.Marshal(&JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil {
|
|
| 52 |
- return append(b, streamNewlineBytes...) |
|
| 53 |
- } |
|
| 54 |
- return []byte("{\"error\":\"format error\"}" + streamNewline)
|
|
| 55 |
- } |
|
| 56 |
- return []byte("Error: " + err.Error() + streamNewline)
|
|
| 57 |
-} |
|
| 58 |
-func (sf *StreamFormatter) FormatProg(id, action string, p interface{}) []byte {
|
|
| 59 |
- switch progress := p.(type) {
|
|
| 60 |
- case *JSONProgress: |
|
| 61 |
- return sf.FormatProgress(id, action, progress) |
|
| 62 |
- case progressreader.PR_JSONProgress: |
|
| 63 |
- return sf.FormatProgress(id, action, &JSONProgress{Current: progress.GetCurrent(), Total: progress.GetTotal()})
|
|
| 64 |
- } |
|
| 65 |
- return nil |
|
| 66 |
-} |
|
| 67 |
-func (sf *StreamFormatter) FormatProgress(id, action string, progress *JSONProgress) []byte {
|
|
| 68 |
- if progress == nil {
|
|
| 69 |
- progress = &JSONProgress{}
|
|
| 70 |
- } |
|
| 71 |
- if sf.json {
|
|
| 72 |
- |
|
| 73 |
- b, err := json.Marshal(&JSONMessage{
|
|
| 74 |
- Status: action, |
|
| 75 |
- ProgressMessage: progress.String(), |
|
| 76 |
- Progress: progress, |
|
| 77 |
- ID: id, |
|
| 78 |
- }) |
|
| 79 |
- if err != nil {
|
|
| 80 |
- return nil |
|
| 81 |
- } |
|
| 82 |
- return b |
|
| 83 |
- } |
|
| 84 |
- endl := "\r" |
|
| 85 |
- if progress.String() == "" {
|
|
| 86 |
- endl += "\n" |
|
| 87 |
- } |
|
| 88 |
- return []byte(action + " " + progress.String() + endl) |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func (sf *StreamFormatter) Json() bool {
|
|
| 92 |
- return sf.json |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-type StdoutFormater struct {
|
|
| 96 |
- io.Writer |
|
| 97 |
- *StreamFormatter |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func (sf *StdoutFormater) Write(buf []byte) (int, error) {
|
|
| 101 |
- formattedBuf := sf.StreamFormatter.FormatStream(string(buf)) |
|
| 102 |
- n, err := sf.Writer.Write(formattedBuf) |
|
| 103 |
- if n != len(formattedBuf) {
|
|
| 104 |
- return n, io.ErrShortWrite |
|
| 105 |
- } |
|
| 106 |
- return len(buf), err |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-type StderrFormater struct {
|
|
| 110 |
- io.Writer |
|
| 111 |
- *StreamFormatter |
|
| 112 |
-} |
|
| 113 |
- |
|
| 114 |
-func (sf *StderrFormater) Write(buf []byte) (int, error) {
|
|
| 115 |
- formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m")
|
|
| 116 |
- n, err := sf.Writer.Write(formattedBuf) |
|
| 117 |
- if n != len(formattedBuf) {
|
|
| 118 |
- return n, io.ErrShortWrite |
|
| 119 |
- } |
|
| 120 |
- return len(buf), err |
|
| 121 |
-} |
| 122 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,67 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "errors" |
|
| 6 |
- "reflect" |
|
| 7 |
- "testing" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func TestFormatStream(t *testing.T) {
|
|
| 11 |
- sf := NewStreamFormatter(true) |
|
| 12 |
- res := sf.FormatStream("stream")
|
|
| 13 |
- if string(res) != `{"stream":"stream"}`+"\r\n" {
|
|
| 14 |
- t.Fatalf("%q", res)
|
|
| 15 |
- } |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-func TestFormatStatus(t *testing.T) {
|
|
| 19 |
- sf := NewStreamFormatter(true) |
|
| 20 |
- res := sf.FormatStatus("ID", "%s%d", "a", 1)
|
|
| 21 |
- if string(res) != `{"status":"a1","id":"ID"}`+"\r\n" {
|
|
| 22 |
- t.Fatalf("%q", res)
|
|
| 23 |
- } |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-func TestFormatSimpleError(t *testing.T) {
|
|
| 27 |
- sf := NewStreamFormatter(true) |
|
| 28 |
- res := sf.FormatError(errors.New("Error for formatter"))
|
|
| 29 |
- if string(res) != `{"errorDetail":{"message":"Error for formatter"},"error":"Error for formatter"}`+"\r\n" {
|
|
| 30 |
- t.Fatalf("%q", res)
|
|
| 31 |
- } |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-func TestFormatJSONError(t *testing.T) {
|
|
| 35 |
- sf := NewStreamFormatter(true) |
|
| 36 |
- err := &JSONError{Code: 50, Message: "Json error"}
|
|
| 37 |
- res := sf.FormatError(err) |
|
| 38 |
- if string(res) != `{"errorDetail":{"code":50,"message":"Json error"},"error":"Json error"}`+"\r\n" {
|
|
| 39 |
- t.Fatalf("%q", res)
|
|
| 40 |
- } |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-func TestFormatProgress(t *testing.T) {
|
|
| 44 |
- sf := NewStreamFormatter(true) |
|
| 45 |
- progress := &JSONProgress{
|
|
| 46 |
- Current: 15, |
|
| 47 |
- Total: 30, |
|
| 48 |
- Start: 1, |
|
| 49 |
- } |
|
| 50 |
- res := sf.FormatProgress("id", "action", progress)
|
|
| 51 |
- msg := &JSONMessage{}
|
|
| 52 |
- if err := json.Unmarshal(res, msg); err != nil {
|
|
| 53 |
- t.Fatal(err) |
|
| 54 |
- } |
|
| 55 |
- if msg.ID != "id" {
|
|
| 56 |
- t.Fatalf("ID must be 'id', got: %s", msg.ID)
|
|
| 57 |
- } |
|
| 58 |
- if msg.Status != "action" {
|
|
| 59 |
- t.Fatalf("Status must be 'action', got: %s", msg.Status)
|
|
| 60 |
- } |
|
| 61 |
- if msg.ProgressMessage != progress.String() {
|
|
| 62 |
- t.Fatalf("ProgressMessage must be %s, got: %s", progress.String(), msg.ProgressMessage)
|
|
| 63 |
- } |
|
| 64 |
- if !reflect.DeepEqual(msg.Progress, progress) {
|
|
| 65 |
- t.Fatal("Original progress not equals progress from FormatProgress")
|
|
| 66 |
- } |
|
| 67 |
-} |
| ... | ... |
@@ -23,6 +23,7 @@ import ( |
| 23 | 23 |
"github.com/docker/docker/pkg/archive" |
| 24 | 24 |
"github.com/docker/docker/pkg/fileutils" |
| 25 | 25 |
"github.com/docker/docker/pkg/ioutils" |
| 26 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 26 | 27 |
"github.com/docker/docker/pkg/stringutils" |
| 27 | 28 |
) |
| 28 | 29 |
|
| ... | ... |
@@ -254,7 +255,7 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
| 254 | 254 |
} |
| 255 | 255 |
|
| 256 | 256 |
func NewHTTPRequestError(msg string, res *http.Response) error {
|
| 257 |
- return &JSONError{
|
|
| 257 |
+ return &jsonmessage.JSONError{
|
|
| 258 | 258 |
Message: msg, |
| 259 | 259 |
Code: res.StatusCode, |
| 260 | 260 |
} |