(ctx context.Context, dst io.Writer, header string, total *int64)
| 68 | } |
| 69 | |
| 70 | func copyProgress(ctx context.Context, dst io.Writer, header string, total *int64) (func(), <-chan struct{}) { |
| 71 | done := make(chan struct{}) |
| 72 | if !streams.NewOut(dst).IsTerminal() { |
| 73 | close(done) |
| 74 | return func() {}, done |
| 75 | } |
| 76 | |
| 77 | fmt.Fprint(dst, aec.Save) |
| 78 | fmt.Fprint(dst, "Preparing to copy...") |
| 79 | |
| 80 | restore := func() { |
| 81 | fmt.Fprint(dst, aec.Restore) |
| 82 | fmt.Fprint(dst, aec.EraseLine(aec.EraseModes.All)) |
| 83 | } |
| 84 | |
| 85 | go func() { |
| 86 | defer close(done) |
| 87 | fmt.Fprint(dst, aec.Hide) |
| 88 | defer fmt.Fprint(dst, aec.Show) |
| 89 | |
| 90 | fmt.Fprint(dst, aec.Restore) |
| 91 | fmt.Fprint(dst, aec.EraseLine(aec.EraseModes.All)) |
| 92 | fmt.Fprint(dst, header) |
| 93 | |
| 94 | var last int64 |
| 95 | fmt.Fprint(dst, progressHumanSize(last)) |
| 96 | |
| 97 | buf := bytes.NewBuffer(nil) |
| 98 | ticker := time.NewTicker(copyProgressUpdateThreshold) |
| 99 | for { |
| 100 | select { |
| 101 | case <-ctx.Done(): |
| 102 | return |
| 103 | case <-ticker.C: |
| 104 | n := atomic.LoadInt64(total) |
| 105 | if n == last { |
| 106 | // Don't write to the terminal, if we don't need to. |
| 107 | continue |
| 108 | } |
| 109 | |
| 110 | // Write to the buffer first to avoid flickering and context switching |
| 111 | fmt.Fprint(buf, aec.Column(uint(len(header)+1))) |
| 112 | fmt.Fprint(buf, aec.EraseLine(aec.EraseModes.Tail)) |
| 113 | fmt.Fprint(buf, progressHumanSize(n)) |
| 114 | |
| 115 | buf.WriteTo(dst) |
| 116 | buf.Reset() |
| 117 | last += n |
| 118 | } |
| 119 | } |
| 120 | }() |
| 121 | return restore, done |
| 122 | } |
| 123 | |
| 124 | // newCopyCommand creates a new `docker cp` command |
| 125 | func newCopyCommand(dockerCLI command.Cli) *cobra.Command { |
no test coverage detected
searching dependent graphs…