"""
This module is the new command of bowl.
Created on 15 March 2014
@author: Charlie Lewis
"""
import ast
import curses
import docker
import importlib
import inspect
import os
import shutil
import sys
import time
import uuid
import wget
from bowl.cli_opts import link
from bowl.cli_opts import repositories
from bowl.cli_opts import services
from bowl.cli_opts import unlink
[docs]class Object(object):
pass
[docs]class new(object):
"""
This class is responsible for the new command of the cli.
"""
# !! TODO needs to implement login if using that
@staticmethod
[docs] def build_dockerfile(self, dockerfile, uuid_dir, main_arg):
# try/catch
with open('/tmp/'+uuid_dir+'/Dockerfile', 'w') as f:
for line in dockerfile:
f.write(line+'\n')
image_tag = 'bowl-'+uuid_dir
if len(self.hosts) == 0:
print "WARNING: No hosts were selected to run these services."
for index, host in enumerate(self.hosts):
# !! TODO try/except - verify that hosts specified can be reached
c = docker.Client(base_url='tcp://'+host['title']+':2375',
version='1.12', timeout=2)
# !! TODO prep, needs to be finished
# build
d_tag = None
d_quiet = False
d_nocache = False
d_rm = False
d_stream = False
d_fileobj=None
d_timeout=None
d_custom_context=False
d_encoding=None
d_tag = "bowl-"+uuid_dir
d_path = '/tmp/'+uuid_dir
# create_container
d_command=None
d_hostname=None
d_user=None
d_detach=False
d_stdin_open=False
d_tty=False
d_mem_limit=0
d_ports=None
d_environment=None
#dns=None
d_volumes=None
#volumes_from=None
d_network_disabled=False
d_name=None
d_entrypoint=None
d_cpu_shares=None
d_working_dir=None
d_memswap_limit=0
d_image = image_tag
# start
d_binds=None
d_port_bindings=None
d_lxc_conf=None
d_publish_all_ports=False
d_links=None
d_privileged=False
d_dns=None
d_dns_search=None
d_volumes_from=None
d_network_mode=None
d_restart_policy=None
d_cap_add=None
d_cap_drop=None
# need to move to the proper location
#d_container = container
#d_links = self.link_names
# !! TODO check that the build actually created an image before trying to create the container
output = c.build(tag="bowl-"+uuid_dir, quiet=False, path='/tmp/'+uuid_dir,
nocache=False, rm=False, stream=False)
# !! TODO cleanup, but for now ensures that the build finishes prior to container creation
print list(output)
# !! TODO check if tty and stdin_open (interactive) are needed
# !! TODO get all args for create_container instead of if/else have args be None
if len(self.names) != 0:
if self.unique:
container = c.create_container(image_tag,
tty=True,
stdin_open=True,
name=self.names[index],
hostname=self.names[index])
else:
container = c.create_container(image_tag,
tty=True,
stdin_open=True,
name=self.names[0],
hostname=self.names[0])
else:
container = c.create_container(image_tag,
tty=True,
stdin_open=True,
name=image_tag,
hostname=image_tag)
# !! TODO get all args for start instead of if/else have args be None
if self.link_names:
c.start(container, publish_all_ports=True, links=self.link_names)
else:
c.start(container, publish_all_ports=True)
try:
directory = main_arg.metadata_path
directory = os.path.expanduser(directory)
with open(os.path.join(directory, "containers"), 'a') as f:
# !! TODO make this a more robust json blob
if len(self.names) != 0:
if self.unique:
f.write("{" +
"'image_id': '"+image_tag+"'," +
" 'container_name': '"+self.names[index]+"'," +
" 'container_id': '"+container['Id']+"'," +
" 'host': '"+host['title']+"'" +
"}\n")
else:
f.write("{" +
"'image_id': '"+image_tag+"'," +
" 'container_name': '"+self.names[0]+"'," +
" 'container_id': '"+container['Id']+"'," +
" 'host': '"+host['title']+"'" +
"}\n")
else:
f.write("{" +
"'image_id': '"+image_tag+"'," +
" 'container_name': '"+image_tag+"'," +
" 'container_id': '"+container['Id']+"'," +
" 'host': '"+host['title']+"'" +
"}\n")
except:
pass
shutil.rmtree('/tmp/'+uuid_dir)
return
@staticmethod
[docs] def build_options(self, main_arg):
menu_dict = {
'title': "Build your bowl",
'type': "menu",
'subtitle': "Please select a choice...",
'options': [
{
'title': "Build Container",
'type': "menu",
'subtitle': "Please select an Operating System...",
'options': []
},
{
'title': "Choose Image",
'type': "menu",
'subtitle': "Please select an Image...",
'options': []
}
]
}
# !! TODO use json.loads/dumps instead of literal_eval
try:
directory = main_arg.metadata_path
directory = os.path.expanduser(directory)
with open(os.path.join(directory, "images"), 'r') as f:
for line in f:
menu_dict['options'][1]['options'].append(ast.literal_eval(line.rstrip('\n')))
except:
pass
launch_dict = {
'title': "Launch Container",
'type': "launch"
}
launch_image_dict = {
'title': "Launch Image(s)",
'type': "launch"
}
args = Object()
args.quiet = False
args.json = True
args.z = True
args.metadata_path = main_arg.metadata_path
all_dict = services.services.main(args)
if 'oses' in all_dict:
os_num = 0
try:
for os_key in all_dict['oses']:
version_num = 0
menu_dict['options'][0]['options'].append(all_dict['oses'][os_key])
for version_key in all_dict['oses'][os_key]['versions']:
menu_dict['options'][0]['options'][os_num]['options'].append(all_dict['oses'][os_key]['versions'][version_key])
database_dict = {
'title': "Databases",
'type': "menu",
'subtitle': "Please select databases...",
'options': []
}
for database_key in all_dict['oses'][os_key]['versions'][version_key]['databases']:
database_dict['options'].append(all_dict['oses'][os_key]['versions'][version_key]['databases'][database_key])
key = os_key+"."+version_key+".databases."+database_key
try:
self.combine_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['databases'][database_key]['combine_cmd']
if self.combine_cmd_dict[key] == "yes":
self.background_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['databases'][database_key]['background_cmd']
except:
print "key error"
environment_dict = {
'title': "Environment Tools",
'type': "menu",
'subtitle': "Please select environment tools...",
'options': []
}
for environment_key in all_dict['oses'][os_key]['versions'][version_key]['environment']:
environment_dict['options'].append(all_dict['oses'][os_key]['versions'][version_key]['environment'][environment_key])
key = os_key+"."+version_key+".environment."+environment_key
try:
self.combine_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['environment'][environment_key]['combine_cmd']
if self.combine_cmd_dict[key] == "yes":
self.background_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['environment'][environment_key]['background_cmd']
except:
print "key error"
service_dict = {
'title': "Services",
'type': "menu",
'subtitle': "Please select services...",
'options': []
}
for service_key in all_dict['oses'][os_key]['versions'][version_key]['services']:
service_dict['options'].append(all_dict['oses'][os_key]['versions'][version_key]['services'][service_key])
key = os_key+"."+version_key+".services."+service_key
try:
self.combine_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['services'][service_key]['combine_cmd']
if self.combine_cmd_dict[key] == "yes":
self.background_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['services'][service_key]['background_cmd']
except:
print "key error"
tool_dict = {
'title': "Tools",
'type': "menu",
'subtitle': "Please select tools...",
'options': []
}
for tool_key in all_dict['oses'][os_key]['versions'][version_key]['tools']:
tool_dict['options'].append(all_dict['oses'][os_key]['versions'][version_key]['tools'][tool_key])
key = os_key+"."+version_key+".tools."+tool_key
try:
self.combine_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['tools'][tool_key]['combine_cmd']
if self.combine_cmd_dict[key] == "yes":
self.background_cmd_dict[key] = all_dict['oses'][os_key]['versions'][version_key]['tools'][tool_key]['background_cmd']
except:
print "key error"
host_dict = {
'title': "Docker Hosts",
'type': "menu",
'subtitle': "Please select which hosts are available to use for containers...",
'options': []
}
try:
directory = main_arg.metadata_path
directory = os.path.expanduser(directory)
with open(os.path.join(directory, "hosts"), 'r') as f:
for line in f:
host_dict['options'].append(ast.literal_eval(line.rstrip('\n')))
except:
pass
menu_dict['options'][0]['options'][os_num]['options'][version_num]['options'].append(database_dict)
menu_dict['options'][0]['options'][os_num]['options'][version_num]['options'].append(environment_dict)
menu_dict['options'][0]['options'][os_num]['options'][version_num]['options'].append(service_dict)
menu_dict['options'][0]['options'][os_num]['options'][version_num]['options'].append(tool_dict)
menu_dict['options'][0]['options'][os_num]['options'][version_num]['options'].append(host_dict)
menu_dict['options'][0]['options'][os_num]['options'][version_num]['options'].append(launch_dict)
version_num += 1
os_num += 1
menu_dict['options'][1]['options'].append(launch_image_dict)
except:
print "failure"
return menu_dict
@staticmethod
[docs] def query_yes_no(self, question, default="no"):
"""Ask a yes/no question via raw_input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "no" (the default), "yes" or None (meaning
an answer is required of the user).
The "answer" return value is one of "yes" or "no".
"""
valid = {"yes":True, "y":True, "ye":True,
"no":False, "n":False}
if default == None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
sys.stdout.write(question + prompt)
choice = raw_input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "\
"(or 'y' or 'n').\n")
# !! TODO break this into more functions
@classmethod
[docs] def main(self, args):
if not os.path.exists(os.path.expanduser(args.metadata_path)):
os.mkdir(os.path.expanduser(args.metadata_path))
if args.toggle_default:
if os.path.exists(os.path.join(os.path.expanduser(args.metadata_path), 'no_default')):
os.remove(os.path.join(os.path.expanduser(args.metadata_path), 'no_default'))
else:
open(os.path.join(os.path.expanduser(args.metadata_path), 'no_default'), 'a').close()
# !! TODO this should be in __init__
# build dictionary of available container options
self.link_names = {}
self.combine_cmd_dict = {}
self.background_cmd_dict = {}
this_dir, this_filename = os.path.split(__file__)
link_path = os.path.join(os.path.dirname(this_dir), "containers/.default/")
link_args = Object()
link_args.z = True
link_args.metadata_path = os.path.expanduser(args.metadata_path)
link_args.SERVICE_HOST = "localhost"
link_args.NAME = "default"
link_args.path = link_path
link.link.main(link_args)
if os.path.exists(os.path.join(os.path.expanduser(args.metadata_path), 'no_default')):
unlink_args = Object()
unlink_args.metadata_path = os.path.expanduser(args.metadata_path)
unlink_args.NAME = "default"
unlink.unlink.main(unlink_args)
menu_dict = self.build_options(self, args)
if not args.no_curses:
self.win = curses.initscr()
self.win.keypad(1)
self.build_dict = {}
self.build_dict['services'] = []
self.hosts = []
self.launch = False
self.exit = False
self.image = False
self.default = True
self.user = False
self.name = False
self.names = []
self.unique = False
self.cmd = False
self.entrypoint = False
self.port = False
self.link = False
self.volume = False
if not args.no_curses:
# init curses stuff
curses.noecho()
curses.cbreak()
curses.start_color()
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
self.menus_dict = {}
# !! TODO cleanup
build_dict = self.build_dict
build_dict = self.process_menu(self, args, menu_dict, build_dict)
self.build_dict = build_dict
curses.endwin()
else:
if args.command:
self.cmd = True
self.default = False
if args.entrypoint:
self.entrypoint = True
self.default = False
if args.volume:
self.volume = True
self.default = False
if args.port:
self.port = True
self.default = False
if args.link:
self.link = True
self.default = False
if args.name:
self.name = True
self.default = False
if args.unique:
self.unique = True
self.default = False
if args.user:
self.user = True
self.default = False
if args.image:
self.image = True
self.launch = True
if args.service:
self.build_dict['services'] = args.service
self.launch = True
if args.host:
for host in args.host:
h = {}
h['type'] = 'choice_menu'
h['title'] = host
self.hosts.append(h)
if self.launch and not args.no_curses:
self.default = self.query_yes_no(self, "Use default runtime settings?", default="yes")
# !! TODO fix this!!!
if not self.default:
if not args.no_curses:
# init curses stuff
curses.noecho()
curses.cbreak()
curses.start_color()
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
# !! TODO only have the option for asking about different parameters if more than one host was selected
# if so, loop through the questions for each container/host
# !! TODO loop through X number of volumes they would like to add
# !! TODO loop through X number of ports in the dockerfile they would like to modify
# !! TODO loop through X number of containers they would like to link to
# !! TODO loop through X number of accounts they would like to add to teh container
# !! TODO if image a user can't be added
# also introspection of ports/volumes might be hard
options_dict = {
'title': "Runtime options",
'type': "menu",
'subtitle': "Please select the runtime options you would like to change...",
'options': [
{
'title': "Do you want to override the CMD of the container?",
'type': "choice_menu",
'options': [
{
'config':'cmd'
}
]
},
{
'title': "Do you want to override the ENTRYPOINT of the container?",
'type': "choice_menu",
'options': [
{
'config':'entrypoint'
}
]
},
{
'title': "Do you want to attach a volume to this container?",
'type': "choice_menu",
'options': [
{
'config':'volume'
}
]
},
{
'title': "Do you want to specify how a port should be exposed?",
'type': "choice_menu",
'options': [
{
'config':'port'
}
]
},
{
'title': "Do you want to link this container to another container?",
'type': "choice_menu",
'options': [
{
'config':'link'
}
]
},
{
'title': "Do you want to name this container?",
'type': "choice_menu",
'options': [
{
'config':'name'
}
]
},
{
'title': "Do you want an account on this container?",
'type': "choice_menu",
'options': [
{
'config':'user'
}
]
}
]
}
if len(self.hosts) > 1:
options_dict['options'].append(
{
'title': "Should the containers use different parameters?",
'type': "choice_menu",
'options': [
{
'config':'unique'
}
]
})
if not args.no_curses:
# !! TODO cleanup
self.config_dict = {}
self.config_dict['services'] = []
config_dict = self.config_dict
config_dict = self.options_menu(self, args, options_dict, config_dict)
self.config_dict = config_dict
curses.endwin()
if self.name:
if self.unique:
for host in self.hosts:
# !! TODO try/except - verify that hosts specified can be reached
c = docker.Client(base_url='tcp://'+host['title']+':2375', version='1.12',
timeout=2)
name = raw_input("Enter container name for container running on "+host['title']+":")
self.names.append(name)
else:
# !! TODO validate
name = raw_input("Enter container name: ")
self.names.append(name)
print self.names
if self.link:
# !! TODO only list containers for each host of which the container is going to be spun up on
# can't link to a container that is not running on the same docker host
containers = []
if self.unique:
for host in self.hosts:
# !! TODO try/except - verify that hosts specified can be reached
c = docker.Client(base_url='tcp://'+host['title']+':2375', version='1.12',
timeout=2)
containers = c.containers()
if len(containers) == 0:
print "There are no running containers on the host "+host['title']+" to link to."
junk = raw_input("Press enter to continue...")
else:
if not args.no_curses:
# init curses stuff
curses.noecho()
curses.cbreak()
curses.start_color()
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
options_dict = {
'title': "Containers to Link",
'type': "menu",
'subtitle': "Please select the containers you would like to link on host "+host['title']+"...",
'options': [
]
}
for container in containers:
container_name = ""
names = container['Names']
# !! TODO names list changes when linked, fix that
for n in names:
container_name += n[1:] + " - "
container_id = container['Id']
container_name += container_id
options_dict['options'].append(
{
'title': '"'+container_name+'"',
'type': "link_choice_menu",
'options': [
{
# !! TODO names list changes when linked, fix that
'link':container['Names'][0][1:]
}
]
}
)
if not args.no_curses:
# !! TODO cleanup
self.config_dict = {}
self.config_dict['services'] = []
config_dict = self.config_dict
config_dict = self.options_menu(self, args, options_dict, config_dict)
self.config_dict = config_dict
curses.endwin()
else:
for host in self.hosts:
# !! TODO try/except - verify that hosts specified can be reached
c = docker.Client(base_url='tcp://'+host['title']+':2375', version='1.12',
timeout=2)
containers.append(c.containers())
print containers
if len(containers) == 0:
print "There are no running containers on the host to link to."
junk = raw_input("Press enter to continue...")
else:
# !! TODO remove containers that are not on all hosts
if not args.no_curses:
# init curses stuff
curses.noecho()
curses.cbreak()
curses.start_color()
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
options_dict = {
'title': "Containers to Link",
'type': "menu",
'subtitle': "Please select the containers you would like to link...",
'options': [
]
}
for host in containers:
for container in host:
container_name = ""
names = container['Names']
# !! TODO names list changes when linked, fix that
for n in names:
container_name += n[1:] + " - "
container_id = container['Id']
container_name += container_id
options_dict['options'].append(
{
'title': '"'+container_name+'"',
'type': "link_choice_menu",
'options': [
{
# !! TODO names list changes when linked, fix that
'link':container['Names'][0][1:]
}
]
}
)
if not args.no_curses:
# !! TODO cleanup
self.config_dict = {}
self.config_dict['services'] = []
config_dict = self.config_dict
config_dict = self.options_menu(self, args, options_dict, config_dict)
self.config_dict = config_dict
curses.endwin()
print self.link
if self.port:
# !! TODO present available exposed ports, and allow each to be assigned specifically
print self.port
if self.volume:
# !! TODO allow n number of runtime volumes (check if needs to be in dockerfile?)
volumes = []
no = True
while no:
volume = raw_input("Enter a volume (path:path): ")
# !! TODO validate input
volumes.append(volume)
no = self.query_yes_no(self, "Would you like to add another volume?", default="no")
print self.volume
print volumes
if self.entrypoint:
# !! TODO allow entrypoint to be overridden
entrypoint = raw_input("Enter new runtime ENTRYPOINT: ")
print entrypoint
if self.cmd:
# !! TODO allow cmd to be overridden
cmd = raw_input("Enter new runtime CMD: ")
print cmd
# !! TODO move out questions
if self.image:
# !! TODO this needs to be tested again
for index, image_info in enumerate(self.build_dict['services']):
image_tag, image_host = image_info.split(",", 1)
# !! TODO try/except - verify that hosts specified can be reached
c = docker.Client(base_url='tcp://'+image_host+':2375', version='1.12',
timeout=2)
cmd = raw_input("Enter command you wish to use for "+image_tag+": ")
# TODO check if tty and stdin_open (interactive) are needed
if len(self.names) != 0:
if self.unique:
container = c.create_container(image_tag, command=cmd, tty=True, stdin_open=True, name=self.names[index], hostname=self.names[index])
else:
container = c.create_container(image_tag, command=cmd, tty=True, stdin_open=True, name=self.names[0], hostname=self.names[0])
else:
container = c.create_container(image_tag, command=cmd, tty=True, stdin_open=True)
c.start(container, publish_all_ports=True)
if self.launch and not self.image:
if self.user:
username = raw_input("Enter username: ")
ssh_pubkey = raw_input("Enter path to ssh public key: ")
# !! TODO if contains an ADD line, be sure and copy additional files
this_dir, this_filename = os.path.split(__file__)
services = self.build_dict['services']
if len(services) == 0:
print "No services were selected."
else:
print "The following services have been selected and will be packaged up into a container: "
# !! TODO parse this out by os/version/type/service
print services
dockerfile = []
num_services = len(services)
envs = {}
workdirs = {}
entrypoints = {}
cmds = {}
uuid_dir = str(uuid.uuid4())
# !! TODO try/except
os.mkdir('/tmp/'+uuid_dir)
for service in sorted(services):
service_orig = service
service = service.split(", ")[0]
envs[service] = []
workdirs[service] = []
entrypoints[service] = []
cmds[service] = []
entrypoint = "/bin/sh -c"
cmd = ""
# !! TODO error check that the array is this size
service_name = service.split(':', 3)
key = ".".join(service_name)
os_flavor = "/".join(service_name[0:3])
repo_args = Object()
repo_args.z = True
repo_args.metadata_path = os.path.expanduser(args.metadata_path)
repos = repositories.repositories.main(repo_args)
try:
copied = 0
for repo in repos:
# !! TODO handle case where this matches on more than one repo
if repo.split(", ")[0].strip() == service_orig.split(", ")[1].strip() and copied == 0:
copied = 1
shutil.copytree(os.path.join(repo.split(", ")[2], os_flavor+"/dockerfiles/"+service_name[3]), '/tmp/'+uuid_dir+"/"+service_name[3])
path = os.path.join(repo.split(", ")[2], os_flavor+"/dockerfiles/"+service_name[3]+"/Dockerfile")
with open(path, 'r') as f:
for line in f:
# remove duplicate lines
if line.rstrip('\n' ) not in dockerfile:
# combine EXPOSE commands
if line.startswith("EXPOSE"):
if any(cmd.startswith("EXPOSE") for cmd in dockerfile):
line = ' '.join(line.rstrip('\n').split(' ', 1)[1:])
# !! TODO make sure this is what the command starts with
# !! TODO display a warning to the user if there is overlapping ports
matching = [s for s in dockerfile if "EXPOSE" in s]
matching.append(line)
dockerfile.remove(matching[0])
dockerfile.append(' '.join(matching))
else:
dockerfile.append(line.rstrip('\n'))
elif line.startswith("ADD"):
# !! TODO check if the add line is a url
add_line = line.rstrip('\n').split()
# cheap hack
if "://" in line:
try:
url = add_line[1]
filename = wget.download(url, out="/tmp/"+uuid_dir+"/"+service_name[3])
filename = "/".join(filename.split('/')[-2:])
dockerfile.append(add_line[0]+" "+filename+" "+add_line[2])
except:
print "Failed to download add statement"
sys.exit(0)
else:
# !! TODO try/except
dockerfile.append(add_line[0]+" "+service_name[3]+"/"+add_line[1]+" "+add_line[2])
elif line.startswith("COPY"):
# !! TODO try/except
copy_line = line.rstrip('\n').split()
dockerfile.append(copy_line[0]+" "+service_name[3]+"/"+copy_line[1]+" "+copy_line[2])
# check for multiple USER commands
elif line.startswith("USER"):
# !! TODO
if num_services == 1:
dockerfile.append(line.rstrip('\n'))
else:
if self.combine_cmd_dict[key] == "yes":
envs[service].append(line.rstrip('\n'))
# !! TODO
#dockerfile.append(line.rstrip('\n'))
elif line.startswith("ENV"):
# !! TODO
if num_services == 1:
dockerfile.append(line.rstrip('\n'))
else:
if self.combine_cmd_dict[key] == "yes":
envs[service].append(line.rstrip('\n'))
# !! TODO
dockerfile.append(line.rstrip('\n'))
# check for multiple WORKDIR commands
elif line.startswith("WORKDIR"):
# !! TODO
if num_services == 1:
dockerfile.append(line.rstrip('\n'))
else:
if self.combine_cmd_dict[key] == "yes":
workdirs[service].append(line.rstrip('\n'))
# !! TODO
dockerfile.append(line.rstrip('\n'))
# check for multiple ENTRYPOINT commands
elif line.startswith("ENTRYPOINT"):
# !! TODO
# check WORKDIR since CMD and ENTRYPOINT are modified
# use the ENTRYPOINT that corresponds with the chosen CMD
if num_services == 1:
dockerfile.append(line.rstrip('\n'))
else:
if self.combine_cmd_dict[key] == "yes":
entrypoints[service].append(line.rstrip('\n'))
entrypoint = (line.rstrip('\n')).split(" ", 1)[1:][0]
# check for multiple CMD commands
elif line.startswith("CMD"):
# !! TODO
# check WORKDIR since CMD and ENTRYPOINT are modified
if num_services == 1:
dockerfile.append(line.rstrip('\n'))
else:
if self.combine_cmd_dict[key] == "yes":
# !! TODO only append if there is only one
# if more than one, use supervisord or something
# if none of them are combine_cmds then use /bin/bash
cmds[service].append(self.background_cmd_dict[key])
#cmds[service].append(line.rstrip('\n'))
#dockerfile.append(line.rstrip('\n'))
cmd = (line.rstrip('\n')).split(" ", 1)[1:][0]
else:
dockerfile.append(line.rstrip('\n'))
except:
print "Dockerfile not found"
# if more than one service with combine_cmd, use supervisord
if len(cmds) > 1:
print "\nFound more than one service that runs a command, installing supervisord..."
dockerfile.append("RUN apt-get install -y supervisor")
for cmd in cmds:
cmd_a = cmd.split(":")
with open("/tmp/"+uuid_dir+"/"+cmd_a[-1]+".conf", 'w') as f:
f.write("[program:"+cmd_a[-1]+"]\n")
f.write("command="+cmds[cmd][0])
dockerfile.append("ADD "+cmd_a[-1]+".conf /etc/supervisor/conf.d/"+cmd_a[-1]+".conf")
if self.user:
try:
# !! TODO try/except
with open(os.path.expanduser(ssh_pubkey), 'r') as fi:
with open("/tmp/"+uuid_dir+"/authorized_keys", 'w') as fo:
for line in fi:
fo.write(line)
dockerfile.append("RUN useradd "+username)
dockerfile.append("RUN mkdir -p /home/"+username+
"/.ssh && chown "+username+
" /home/"+username+
"/.ssh && chmod 700 /home/"+username+"/.ssh")
dockerfile.append("ADD authorized_keys /home/"+username+"/.ssh/authorized_keys")
# !! TODO ask if user needs sudo
dockerfile.append("RUN apt-get install -y sudo")
dockerfile.append('RUN echo "'+username+
' ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers')
# !! TODO ask if more than one user
except:
print "SSH Key file not found, failed to create user"
print "\n### GENERATED DOCKERFILE ###"
cmd = False
for line in dockerfile:
if "CMD" in line:
cmd = True
if not cmd:
if len(cmds) > 1:
# !! TODO check user and workdir
dockerfile.append("CMD /usr/bin/supervisord -n")
else:
dockerfile.append("CMD /bin/bash")
for line in dockerfile:
print line
print "### END GENERATED DOCKERFILE ###\n"
self.build_dockerfile(self, dockerfile, uuid_dir, args)
print self.link_names
@staticmethod
@staticmethod
@staticmethod