Improve logging and prepare for gate
Switched all print statements to use logger Added ability to halt operations via Ctrl-C, needs cleanup handler Added realtime logging when using only one thread (matches bash building scripts behavior) main is now returning all the build image statuses, so a quick test script would look like: import build res = build.main() if len(res[0]): LOG.error('failure') Change-Id: Ic8000a96573b011490dc330a4512c77c602ac3d2 Partially-Implements: blueprint build-script
This commit is contained in:
parent
6bca976500
commit
485e1b2539
@ -17,15 +17,16 @@
|
|||||||
# TODO(SamYaple): Build only missing images
|
# TODO(SamYaple): Build only missing images
|
||||||
# TODO(SamYaple): Execute the source install script that will pull
|
# TODO(SamYaple): Execute the source install script that will pull
|
||||||
# down and create tarball
|
# down and create tarball
|
||||||
# TODO(SamYaple): Improve logging instead of printing to stdout
|
# TODO(jpeeler): Add clean up handler for SIGINT
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import Queue
|
import Queue
|
||||||
import shutil
|
import shutil
|
||||||
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -35,13 +36,20 @@ import traceback
|
|||||||
import docker
|
import docker
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
|
logging.basicConfig()
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
LOG.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
|
||||||
|
|
||||||
class WorkerThread(Thread):
|
class WorkerThread(Thread):
|
||||||
|
|
||||||
def __init__(self, queue, nocache, keep):
|
def __init__(self, queue, nocache, keep, threads):
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.nocache = nocache
|
self.nocache = nocache
|
||||||
self.forcerm = not keep
|
self.forcerm = not keep
|
||||||
|
self.threads = threads
|
||||||
self.dc = docker.Client(**docker.utils.kwargs_from_env())
|
self.dc = docker.Client(**docker.utils.kwargs_from_env())
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
@ -59,7 +67,7 @@ class WorkerThread(Thread):
|
|||||||
self.queue.task_done()
|
self.queue.task_done()
|
||||||
|
|
||||||
def builder(self, image):
|
def builder(self, image):
|
||||||
print('Processing:', image['name'])
|
LOG.info('Processing: {}'.format(image['name']))
|
||||||
image['status'] = "building"
|
image['status'] = "building"
|
||||||
|
|
||||||
if (image['parent'] is not None and
|
if (image['parent'] is not None and
|
||||||
@ -81,12 +89,20 @@ class WorkerThread(Thread):
|
|||||||
|
|
||||||
if 'stream' in stream:
|
if 'stream' in stream:
|
||||||
image['logs'] = image['logs'] + stream['stream']
|
image['logs'] = image['logs'] + stream['stream']
|
||||||
elif 'errorDetail' in stream:
|
if self.threads == 1:
|
||||||
|
LOG.info('{}:{}'.format(image['name'],
|
||||||
|
stream['stream'].rstrip()))
|
||||||
|
if 'errorDetail' in stream:
|
||||||
image['status'] = "error"
|
image['status'] = "error"
|
||||||
|
LOG.error(stream['errorDetail']['message'])
|
||||||
raise Exception(stream['errorDetail']['message'])
|
raise Exception(stream['errorDetail']['message'])
|
||||||
|
|
||||||
image['status'] = "built"
|
image['status'] = "built"
|
||||||
print(image['logs'], 'Processed:', image['name'])
|
|
||||||
|
if self.threads == 1:
|
||||||
|
LOG.info('Processed: {}'.format(image['name']))
|
||||||
|
else:
|
||||||
|
LOG.info('{}Processed: {}'.format(image['logs'], image['name']))
|
||||||
|
|
||||||
|
|
||||||
def argParser():
|
def argParser():
|
||||||
@ -124,13 +140,18 @@ def argParser():
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
default=False)
|
default=False)
|
||||||
parser.add_argument('-T', '--threads',
|
parser.add_argument('-T', '--threads',
|
||||||
help='The number of threads to use while building',
|
help='The number of threads to use while building.'
|
||||||
|
' (Note: setting to one will allow real time'
|
||||||
|
' logging.)',
|
||||||
type=int,
|
type=int,
|
||||||
default=8)
|
default=8)
|
||||||
parser.add_argument('--template',
|
parser.add_argument('--template',
|
||||||
help='Create dockerfiles from templates',
|
help='Create dockerfiles from templates',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False)
|
default=False)
|
||||||
|
parser.add_argument('-d', '--debug',
|
||||||
|
help='Turn on debugging log level',
|
||||||
|
action='store_true')
|
||||||
return vars(parser.parse_args())
|
return vars(parser.parse_args())
|
||||||
|
|
||||||
|
|
||||||
@ -148,6 +169,9 @@ class KollaWorker(object):
|
|||||||
self.tag = args['tag']
|
self.tag = args['tag']
|
||||||
self.prefix = self.base + '-' + self.type_ + '-'
|
self.prefix = self.base + '-' + self.type_ + '-'
|
||||||
|
|
||||||
|
self.image_statuses_bad = {}
|
||||||
|
self.image_statuses_good = {}
|
||||||
|
|
||||||
def setupWorkingDir(self):
|
def setupWorkingDir(self):
|
||||||
"""Creates a working directory for use while building"""
|
"""Creates a working directory for use while building"""
|
||||||
ts = time.time()
|
ts = time.time()
|
||||||
@ -158,6 +182,7 @@ class KollaWorker(object):
|
|||||||
shutil.copytree(self.templates_dir, self.working_dir)
|
shutil.copytree(self.templates_dir, self.working_dir)
|
||||||
else:
|
else:
|
||||||
shutil.copytree(self.images_dir, self.working_dir)
|
shutil.copytree(self.images_dir, self.working_dir)
|
||||||
|
LOG.debug('Created working dir: {}'.format(self.working_dir))
|
||||||
|
|
||||||
def createDockerfiles(self):
|
def createDockerfiles(self):
|
||||||
for path in self.docker_build_paths:
|
for path in self.docker_build_paths:
|
||||||
@ -185,6 +210,9 @@ class KollaWorker(object):
|
|||||||
for root, dirs, names in os.walk(path):
|
for root, dirs, names in os.walk(path):
|
||||||
if filename in names:
|
if filename in names:
|
||||||
self.docker_build_paths.append(root)
|
self.docker_build_paths.append(root)
|
||||||
|
LOG.debug('Found {}'.format(root.split(self.working_dir)[1]))
|
||||||
|
|
||||||
|
LOG.debug('Found {} Dockerfiles'.format(len(self.docker_build_paths)))
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
"""Remove temp files"""
|
"""Remove temp files"""
|
||||||
@ -203,21 +231,24 @@ class KollaWorker(object):
|
|||||||
if image['parent'] is None:
|
if image['parent'] is None:
|
||||||
self.tiers[-1].append(image)
|
self.tiers[-1].append(image)
|
||||||
processed_images.append(image)
|
processed_images.append(image)
|
||||||
|
LOG.debug('Sorted parentless image: {}'.format(
|
||||||
|
image['name']))
|
||||||
if len(self.tiers) > 1:
|
if len(self.tiers) > 1:
|
||||||
for parent in self.tiers[-2]:
|
for parent in self.tiers[-2]:
|
||||||
if image['parent'] == parent['fullname']:
|
if image['parent'] == parent['fullname']:
|
||||||
image['parent'] = parent
|
image['parent'] = parent
|
||||||
self.tiers[-1].append(image)
|
self.tiers[-1].append(image)
|
||||||
processed_images.append(image)
|
processed_images.append(image)
|
||||||
|
LOG.debug('Sorted image {} with parent {}'.format(
|
||||||
|
image['name'], parent['fullname']))
|
||||||
|
LOG.debug('===')
|
||||||
# TODO(SamYaple): Improve error handling in this section
|
# TODO(SamYaple): Improve error handling in this section
|
||||||
if not processed_images:
|
if not processed_images:
|
||||||
print('Could not find parent image from some images. Aborting',
|
LOG.warning('Could not find parent image from some images.'
|
||||||
file=sys.stderr)
|
' Aborting')
|
||||||
for image in images_to_process:
|
for image in images_to_process:
|
||||||
print(image['name'], image['parent'], file=sys.stderr)
|
LOG.warning('{} {}'.format(image['name'], image['parent']))
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
# You cannot modify a list while using the list in a for loop as it
|
# You cannot modify a list while using the list in a for loop as it
|
||||||
# will produce unexpected results by messing up the index so we
|
# will produce unexpected results by messing up the index so we
|
||||||
# build a seperate list and remove them here instead
|
# build a seperate list and remove them here instead
|
||||||
@ -225,19 +256,28 @@ class KollaWorker(object):
|
|||||||
images_to_process.remove(image)
|
images_to_process.remove(image)
|
||||||
|
|
||||||
def summary(self):
|
def summary(self):
|
||||||
"""Walk the list of images and check for errors"""
|
"""Walk the dictionary of images statuses and print results"""
|
||||||
print("Successfully built images")
|
self.get_image_statuses()
|
||||||
print("=========================")
|
LOG.info("Successfully built images")
|
||||||
|
LOG.info("=========================")
|
||||||
|
for name in self.image_statuses_good.keys():
|
||||||
|
LOG.info(name)
|
||||||
|
|
||||||
|
LOG.info("Images that failed to build")
|
||||||
|
LOG.info("===========================")
|
||||||
|
for name, status in self.image_statuses_bad.iteritems():
|
||||||
|
LOG.error('{}\r\t\t\t Failed with status: {}'.format(
|
||||||
|
name, status))
|
||||||
|
|
||||||
|
def get_image_statuses(self):
|
||||||
|
if len(self.image_statuses_bad) or len(self.image_statuses_good):
|
||||||
|
return (self.image_statuses_bad, self.image_statuses_good)
|
||||||
for image in self.images:
|
for image in self.images:
|
||||||
if image['status'] == "built":
|
if image['status'] == "built":
|
||||||
print(image['name'])
|
self.image_statuses_good[image['name']] = image['status']
|
||||||
|
else:
|
||||||
print("\nImages that failed to build")
|
self.image_statuses_bad[image['name']] = image['status']
|
||||||
print("===========================")
|
return (self.image_statuses_bad, self.image_statuses_good)
|
||||||
for image in self.images:
|
|
||||||
if image['status'] != "built":
|
|
||||||
print(image['name'], "\r\t\t\t Failed with status:",
|
|
||||||
image['status'])
|
|
||||||
|
|
||||||
def buildImageList(self):
|
def buildImageList(self):
|
||||||
self.images = list()
|
self.images = list()
|
||||||
@ -274,10 +314,11 @@ class KollaWorker(object):
|
|||||||
self.sortImages()
|
self.sortImages()
|
||||||
|
|
||||||
pools = list()
|
pools = list()
|
||||||
for tier in self.tiers:
|
for count, tier in enumerate(self.tiers):
|
||||||
pool = Queue.Queue()
|
pool = Queue.Queue()
|
||||||
for image in tier:
|
for image in tier:
|
||||||
pool.put(image)
|
pool.put(image)
|
||||||
|
LOG.debug('Tier {}: add image {}'.format(count, image['name']))
|
||||||
|
|
||||||
pools.append(pool)
|
pools.append(pool)
|
||||||
|
|
||||||
@ -299,11 +340,13 @@ def push_image(image):
|
|||||||
print(stream['stream'])
|
print(stream['stream'])
|
||||||
elif 'errorDetail' in stream:
|
elif 'errorDetail' in stream:
|
||||||
image['status'] = "error"
|
image['status'] = "error"
|
||||||
raise Exception(stream['errorDetail']['message'])
|
LOG.error(stream['errorDetail']['message'])
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = argParser()
|
args = argParser()
|
||||||
|
if args['debug']:
|
||||||
|
LOG.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
kolla = KollaWorker(args)
|
kolla = KollaWorker(args)
|
||||||
kolla.setupWorkingDir()
|
kolla.setupWorkingDir()
|
||||||
@ -317,7 +360,8 @@ def main():
|
|||||||
# Returns a list of Queues for us to loop through
|
# Returns a list of Queues for us to loop through
|
||||||
for pool in pools:
|
for pool in pools:
|
||||||
for x in xrange(args['threads']):
|
for x in xrange(args['threads']):
|
||||||
WorkerThread(pool, args['no_cache'], args['keep']).start()
|
WorkerThread(pool, args['no_cache'], args['keep'],
|
||||||
|
args['threads']).start()
|
||||||
# block until queue is empty
|
# block until queue is empty
|
||||||
pool.join()
|
pool.join()
|
||||||
|
|
||||||
@ -330,5 +374,7 @@ def main():
|
|||||||
kolla.summary()
|
kolla.summary()
|
||||||
kolla.cleanup()
|
kolla.cleanup()
|
||||||
|
|
||||||
|
return kolla.get_image_statuses()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user