| package utils // import "github.com/docker/docker/distribution/utils" | |
| import ( | |
| "io" | |
| "net" | |
| "os" | |
| "syscall" | |
| "github.com/docker/docker/pkg/progress" | |
| "github.com/docker/docker/pkg/streamformatter" | |
| "github.com/sirupsen/logrus" | |
| ) | |
| // WriteDistributionProgress is a helper for writing progress from chan to JSON | |
| // stream with an optional cancel function. | |
| func WriteDistributionProgress(cancelFunc func(), outStream io.Writer, progressChan <-chan progress.Progress) { | |
| progressOutput := streamformatter.NewJSONProgressOutput(outStream, false) | |
| operationCancelled := false | |
| for prog := range progressChan { | |
| if err := progressOutput.WriteProgress(prog); err != nil && !operationCancelled { | |
| // don't log broken pipe errors as this is the normal case when a client aborts | |
| if isBrokenPipe(err) { | |
| logrus.Info("Pull session cancelled") | |
| } else { | |
| logrus.Errorf("error writing progress to client: %v", err) | |
| } | |
| cancelFunc() | |
| operationCancelled = true | |
| // Don't return, because we need to continue draining | |
| // progressChan until it's closed to avoid a deadlock. | |
| } | |
| } | |
| } | |
| func isBrokenPipe(e error) bool { | |
| if netErr, ok := e.(*net.OpError); ok { | |
| e = netErr.Err | |
| if sysErr, ok := netErr.Err.(*os.SyscallError); ok { | |
| e = sysErr.Err | |
| } | |
| } | |
| return e == syscall.EPIPE | |
| } |