require_relative 'config' require_relative 'dfile' require_relative 'docker_cli' require_relative 'cli/command_factory' module Dockerun module DSL include TR::CondUtils def set_exec_root(root) @_exec_root = root end def exec_root @_exec_root end def config(&block) _config.instance_eval(&block) end def set_dry_run_mode(bool = true, prompt = false) _config.dry_run(bool, prompt) end # Main DSL for dockerfile related configurations def from(base_image, &block) _dfile.base_image = base_image _dfile.instance_eval(&block) end # Additional dockerfile configurations for example # if user did not specify the image name, better generate # by system so that it can be traced for subsequent functions # later such as delete image def dockerfile_patch(&block) _file.instance_eval(&block) end # Main DSL for docker cli def up(&block) _docker_cli.instance_eval(&block) end # Trigger point to start the ball rolling! def go # Link context with Dockerfile and docker cli if not_empty?(_config.active_context) self.listener do |ops, *val| _config.active_context.call(ops, *val) end _dfile.listener do |ops, *val| _config.active_context.call(ops, *val) end _docker_cli.listener do |ops, *val| _config.active_context.call(ops, *val) end end # give chance to context to verify the integrity of the # workspace before proceed trigger_listener(:dsl_before_go, self) # check if image exist? if not image_exist? logger.debug "Image doesn't exist... proceed to build the image" # if not exist, build the image giving the name st, res = build_image if st trigger_listener(:dsl_image_built, self) #puts " Image '#{image_name}' built successfully" else trigger_listener(:dsl_image_building_failed, self) raise Error, " Image '#{image_name}' failed to be built. Error was : #{res.err_lines.join("\n")}" end else logger.debug "Image exist... skipped image building..." end # check if container exist? if container_exist? trigger_listener(:dsl_before_start_container, self) logger.debug "Container exist... Starting container..." # container exist, start the container start_container trigger_listener(:dsl_before_attach_container, self) logger.debug "Container started... attaching container..." # attach to container attach_container trigger_listener(:dsl_after_attach_container, self) else trigger_listener(:dsl_before_run_container, self) logger.debug "Container does not exist. Creating new container..." # Container does not exist, run the container run_container end end def image_name if _dfile.is_image_name_given? _dfile.image_name else _dfile.generated_image_name end end def container_name if _docker_cli.is_container_name_given? _docker_cli.container_name else _docker_cli.generated_container_name end end def listener(&block) @listener = block end private def _config if @_config.nil? @_config = Config.new end @_config end def _dfile if @_dfile.nil? @_dfile = Dfile.new(_config) end @_dfile end def _docker_cli if @_docker_cli.nil? @_docker_cli = DockerCli.new(_config, _dfile) end @_docker_cli end def docker_cf if @_docker_cf.nil? @_docker_cf = Dockerun::Cli::CommandFactory.new end @_docker_cf end def trigger_listener(evt, *args, &block) if not @listener.nil? @listener.call(evt, *args, &block) end end # flag to bridge between script and eval mode def mark_go_done @_go_done = true end def is_go_done? @_go_done.nil? ? false : @_go_done end # end flag def image_exist? fact = docker_cf.find_image(image_name) if _config.is_dry_run? dry_run_puts "Find Image : #{fact.to_s}" dry_run_puts "Find Image : Returning false to allow image build" false else res = docker_cf.find_image(image_name).run res.success? and not_empty?(res.output_lines) end end def build_image dfile = _dfile.generate dfileName = File.join(exec_root,"Dockerfile-#{SecureRandom.hex(4)}") File.open(dfileName,"w") do |f| f.write dfile end fact = docker_cf.build_image(image_name, dockerfile: dfileName, working_dir: exec_root) if _config.is_gen_docker_runscript? out = File.join(exec_root,"1.build-image.sh") if (not File.exist?(out)) or _config.is_overwrite_docker_runscript? File.open(out, "w") do |f| f.puts "#!/bin/sh" f.puts fact.to_s end end end if _config.is_dry_run? dry_run_puts "Build Image : #{fact.to_s}" dry_run_puts "Build Image : Returning true to proceed to next phase." dry_run_puts "Generated dockerfile '#{dfileName}' shall be remained in project directory." [true, nil] else res = fact.run(true) if res.success? FileUtils.rm(dFileName) if not _dfile.keep_dockerfile? [true, res] else [false, res] end end end def container_exist? if _docker_cli.keep_container? fact = docker_cf.find_from_all_container(container_name) if _config.is_dry_run? dry_run_puts "Is container exist? : #{fact.to_s}" dry_run_puts "Returning false to allow container build" false else res = fact.run res.success? and not_empty?(res.output_lines) end else remove_container false end end def remove_container fact = docker_cf.find_from_all_container(container_name) res = fact.run if res.success? res.output_lines.each do |ci| docker_cf.stop_container(ci).run r = docker_cf.delete_container(ci).run logger.debug "Delete container : #{r.success?} / #{r.success? ? r.output_lines.join("\n") : r.err_lines.join("\n")}" end else logger.debug "Failed to find all container by name '#{container_name}'" end end def start_container fact = docker_cf.find_running_container(container_name) fact2 = docker_cf.start_container(container_name) if _config.is_gen_docker_runscript? out = File.join(exec_root,"3.start-container.sh") if (not File.exist?(out)) or _config.is_overwrite_docker_runscript? File.open(out, "w") do |f| f.puts "#!/bin/sh" f.puts fact.to_s end end end if _config.is_dry_run? dry_run_puts "Find running container : #{fact.to_s}" dry_run_puts "Start container : #{fact2.to_s}" [true, nil] else res = fact.run if res.success? and not_empty?(res.output_lines) # already running so ignore [true, res] else res2 = fact2.run [res2.success?, res2] end end end def attach_container fact = docker_cf.attach_container(container_name) if _config.is_gen_docker_runscript? out = File.join(exec_root,"4.attach-container.sh") if (not File.exist?(out)) or _config.is_overwrite_docker_runscript? File.open(out, "w") do |f| f.puts "#!/bin/sh" f.puts fact.to_s end end end if _config.is_dry_run? dry_run_puts "Attach container : #{fact.to_s}" else fact.run end end def run_container params = { interactive: _docker_cli.is_interactive?, tty: _docker_cli.is_interactive?, del: !_docker_cli.keep_container?, container_name: container_name, match_user: _config.set_current_user?, command: _docker_cli.command_to_be_run, mounts: _docker_cli.mounts } params[:ports] = {} if params[:ports].nil? params[:ports].merge!(_docker_cli.expose_ports) if _docker_cli.has_exposed_ports? rparams = _docker_cli.trigger_listener(:docker_cli_construct_command, params) rparams = params if is_empty?(rparams) fact = docker_cf.create_container_from_image(image_name, rparams) if _config.is_gen_docker_runscript? out = File.join(exec_root,"2.create-container.sh") if (not File.exist?(out)) or _config.is_overwrite_docker_runscript? File.open(out, "w") do |f| f.puts "#!/bin/sh" f.puts fact.to_s end end end if _config.is_dry_run? dry_run_puts "Run container : #{fact.to_s}" else fact.run end end def dry_run_puts(msg) puts "[DryRun] #{msg}" if _config.is_dry_run_prompt? end def logger Dockerun.logger(:dockerun_dsl) end end end