|  | @@ -5,8 +5,8 @@ import logging
 | 
	
		
			
				|  |  |  import re
 | 
	
		
			
				|  |  |  import os
 | 
	
		
			
				|  |  |  import sys
 | 
	
		
			
				|  |  | -import json
 | 
	
		
			
				|  |  |  from .container import Container
 | 
	
		
			
				|  |  | +from .progress_stream import stream_output, StreamOutputError
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  log = logging.getLogger(__name__)
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -343,84 +343,6 @@ class Service(object):
 | 
	
		
			
				|  |  |          return True
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class StreamOutputError(Exception):
 | 
	
		
			
				|  |  | -    pass
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -def stream_output(output, stream):
 | 
	
		
			
				|  |  | -    is_terminal = hasattr(stream, 'fileno') and os.isatty(stream.fileno())
 | 
	
		
			
				|  |  | -    all_events = []
 | 
	
		
			
				|  |  | -    lines = {}
 | 
	
		
			
				|  |  | -    diff = 0
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    for chunk in output:
 | 
	
		
			
				|  |  | -        event = json.loads(chunk)
 | 
	
		
			
				|  |  | -        all_events.append(event)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if 'progress' in event or 'progressDetail' in event:
 | 
	
		
			
				|  |  | -            image_id = event['id']
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if image_id in lines:
 | 
	
		
			
				|  |  | -                diff = len(lines) - lines[image_id]
 | 
	
		
			
				|  |  | -            else:
 | 
	
		
			
				|  |  | -                lines[image_id] = len(lines)
 | 
	
		
			
				|  |  | -                stream.write("\n")
 | 
	
		
			
				|  |  | -                diff = 0
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if is_terminal:
 | 
	
		
			
				|  |  | -                # move cursor up `diff` rows
 | 
	
		
			
				|  |  | -                stream.write("%c[%dA" % (27, diff))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        print_output_event(event, stream, is_terminal)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if 'id' in event and is_terminal:
 | 
	
		
			
				|  |  | -            # move cursor back down
 | 
	
		
			
				|  |  | -            stream.write("%c[%dB" % (27, diff))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        stream.flush()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return all_events
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -def print_output_event(event, stream, is_terminal):
 | 
	
		
			
				|  |  | -    if 'errorDetail' in event:
 | 
	
		
			
				|  |  | -        raise StreamOutputError(event['errorDetail']['message'])
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    terminator = ''
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if is_terminal and 'stream' not in event:
 | 
	
		
			
				|  |  | -        # erase current line
 | 
	
		
			
				|  |  | -        stream.write("%c[2K\r" % 27)
 | 
	
		
			
				|  |  | -        terminator = "\r"
 | 
	
		
			
				|  |  | -        pass
 | 
	
		
			
				|  |  | -    elif 'progressDetail' in event:
 | 
	
		
			
				|  |  | -        return
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if 'time' in event:
 | 
	
		
			
				|  |  | -        stream.write("[%s] " % event['time'])
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if 'id' in event:
 | 
	
		
			
				|  |  | -        stream.write("%s: " % event['id'])
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if 'from' in event:
 | 
	
		
			
				|  |  | -        stream.write("(from %s) " % event['from'])
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    status = event.get('status', '')
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if 'progress' in event:
 | 
	
		
			
				|  |  | -        stream.write("%s %s%s" % (status, event['progress'], terminator))
 | 
	
		
			
				|  |  | -    elif 'progressDetail' in event:
 | 
	
		
			
				|  |  | -        detail = event['progressDetail']
 | 
	
		
			
				|  |  | -        if 'current' in detail:
 | 
	
		
			
				|  |  | -            percentage = float(detail['current']) / float(detail['total']) * 100
 | 
	
		
			
				|  |  | -            stream.write('%s (%.1f%%)%s' % (status, percentage, terminator))
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            stream.write('%s%s' % (status, terminator))
 | 
	
		
			
				|  |  | -    elif 'stream' in event:
 | 
	
		
			
				|  |  | -        stream.write("%s%s" % (event['stream'], terminator))
 | 
	
		
			
				|  |  | -    else:
 | 
	
		
			
				|  |  | -        stream.write("%s%s\n" % (status, terminator))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 |