forked from giantmonkey/docker-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dt-rebuild-image
executable file
·120 lines (95 loc) · 3.74 KB
/
dt-rebuild-image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/python
import argparse
import subprocess
import time
import pipes
import os
import re
import docker
def parse_arguments():
class DefaultList(list):
def __copy__(self):
return []
parser = argparse.ArgumentParser(description="Rebuild a docker image from the corresponding Dockerfile", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("image", type=str, help="image name")
parser.add_argument("--dir", type=str, default=DefaultList([os.path.expanduser("~/docker-files"), "/var/lib/docker-files"]), action="append", dest="dirs", help="base directory that contains directories with Dockerfiles")
parser.add_argument("--include-parents", default=True, action="store_true", help="rebuild all parent images")
parser.add_argument("--include-descendants", action="store_true", help="rebuild all descendant images")
parser.add_argument("--force", action="store_true", help="force rebuilding of images even if timestamps are newer")
parser.add_argument("--dry-run", action="store_true", help="only print to stdout instead of writing to file")
return parser.parse_args()
def get_docker_file_path(args, image):
for dir in args.dirs:
parts = image.split("/")
# consider full image name and last part of image name if there is a /
subdirs = filter(bool, [parts] + [parts[1:]])
for subdir in subdirs:
path = os.path.join(dir, *(subdir + ["Dockerfile"]))
if os.path.exists(path):
return path
raise RuntimeError("cannot find Dockerfile")
def get_parent_images(args):
images = []
image = args.image
while image:
with open(get_docker_file_path(args, image)) as f:
match = re.search(r"^FROM\s+([^\s]+/[^\s]+)", f.read())
if match:
image = match.group(1)
images.append(image)
else:
# stop is there is no FROM line or we have an image without prefix
image = None
return images
def get_descendant_images(args):
# find all docker files with FROM args.image
descendants = []
images = [args.image]
while images:
image = images.pop()
for dir in args.dirs:
if os.path.exists(dir):
output = subprocess.check_output("find %s -name Dockerfile -exec grep -l \"FROM\\s*%s\" {} \\;" % (pipes.quote(dir), pipes.quote(image)), shell=True)
# extract image names from file paths
children = [os.path.relpath(os.path.dirname(path), dir) for path in output.split("\n") if path]
descendants += children
# continue search
images += children
return descendants
def rebuild_image(args, client, image):
directory = os.path.dirname(get_docker_file_path(args, image))
if args.force:
rebuild = True
else:
directory_mtime = os.path.getmtime(directory)
try:
inspect = client.inspect_image(image)
created_at_str = inspect["created"].split(".")[0]
# we currently throw away some precision when parsing the created at string
image_created_at = time.mktime(time.strptime(created_at_str, "%Y-%m-%dT%H:%M:%S"))
except docker.client.APIError:
image_created_at = 0
print "Error: Could not inspect image", image, "with docker"
print "Assuming image", image, "is not uptodate"
rebuild = image_created_at <= directory_mtime
if rebuild:
print "Building image", image, "(%s)" % directory, "..."
if not args.dry_run:
id, output = client.build(directory, image, quiet=True)
if not id:
print output
else:
print "Skipping build of uptodate image", image, "(%s)" % directory
def rebuild_images(args, client):
images = [args.image]
if args.include_parents:
images += get_parent_images(args)
if args.include_descendants:
descendants = get_descendant_images(args)
descendants.reverse()
images = descendants + images
for image in reversed(images):
rebuild_image(args, client, image)
args = parse_arguments()
client = docker.Client()
rebuild_images(args, client)