lib/cide.rb in cide-0.0.8 vs lib/cide.rb in cide-0.1.0

- old
+ new

@@ -1,38 +1,31 @@ require 'erb' require 'json' require 'optparse' -require 'shellwords' require 'time' require 'yaml' require 'thor' +require 'cide/docker' + # CIDE is a Continuous Integration Docker Environment runner # # The juicy bits are defined in CIDE::CLI module CIDE + DIR = File.expand_path('..', __FILE__) DOCKERFILE = 'Dockerfile' - SSH_CONFIG_FILE = 'ssh_config' TEMP_SSH_KEY = 'id_rsa.tmp' - DOCKERFILE_TEMPLATE = File.read( - File.expand_path('../cide_template.erb', __FILE__), - ) - SSH_CONFIG_CONTENTS = File.read(File.expand_path('../ssh_config', __FILE__)) + DOCKERFILE_TEMPLATE = File.join(DIR, 'cide_template.erb') + SSH_CONFIG_FILE = 'ssh_config' + SSH_CONFIG_PATH = File.join(DIR, SSH_CONFIG_FILE) CONFIG_FILE = '.cide.yml' CIDE_DIR = '/cide' CIDE_SRC_DIR = File.join(CIDE_DIR, '/src') CIDE_SSH_DIR = File.join(CIDE_DIR, '/.ssh') - module_function - - def docker_id(str) - # Replaces invalid docker tag characters by underscores - "#{str}".downcase.gsub(/[^a-z0-9\-_.]/, '_') - end - def self.struct(opts = {}, &block) Class.new(Struct.new(*opts.keys), &block).new(*opts.values) end DefaultConfig = struct( @@ -43,22 +36,27 @@ before: {}, export: false, export_dir: './artifacts', host_export_dir: nil, run: 'script/ci', - ssh_key: nil, + use_ssh: false, + ssh_key: '~/.ssh/id_rsa', ) do alias_method :image=, :from= alias_method :command=, :run= def name=(str) - super CIDE.docker_id(str) + super CIDE::Docker.id(str) end + def ssh_key_path + File.expand_path(ssh_key) + end + def to_dockerfile - ERB.new(DOCKERFILE_TEMPLATE, nil, '<>-').result(binding) + ERB.new(File.read(DOCKERFILE_TEMPLATE), nil, '<>-').result(binding) end def merge!(opts = {}) opts.each_pair { |k, v| public_send("#{k}=", v) } self @@ -76,12 +74,13 @@ end end # Command-line option-parsing and execution for cide class CLI < Thor - include CIDE + include CIDE::Docker include Thor::Actions + add_runtime_options! default_command 'build' desc 'build', 'Builds an image and executes the run script' @@ -104,13 +103,13 @@ desc: 'The script to run', aliases: ['r'], default: nil method_option 'ssh_key', - desc: 'The ssh key to put into the docker image', + desc: 'Path to a ssh key to import into the docker image', aliases: ['s'], - default: nil + default: '~/.ssh/id_rsa' def build setup_docker config = DefaultConfig.merge YAML.load_file(CONFIG_FILE) @@ -120,45 +119,23 @@ config.name ||= File.basename(Dir.pwd) config.host_export_dir ||= config.export_dir tag = "cide/#{config.name}" - if config.ssh_key && File.exist?(config.ssh_key) - say_status :SSHkey, 'Creating temp ssh key file within directory' - ssh_key_contents = File.read(config.ssh_key) - File.write(TEMP_SSH_KEY, ssh_key_contents) - config.ssh_key = TEMP_SSH_KEY - at_exit do - File.unlink(TEMP_SSH_KEY) + if config.use_ssh + unless File.exist?(config.ssh_key_path) + fail MalformattedArgumentError, "SSH key #{config.ssh_key} not found" end - else - say_status :SSHKey, 'No SSH key specified' + + create_tmp_file SSH_CONFIG_FILE, File.read(SSH_CONFIG_PATH) + create_tmp_file TEMP_SSH_KEY, File.read(config.ssh_key_path) end say_status :config, config.to_h - # FIXME: Move Dockerfile out of the way if it exists - if !File.exist?(DOCKERFILE) - say_status :Dockerfile, 'Creating temporary Dockerfile' - File.write(DOCKERFILE, config.to_dockerfile) - at_exit do - File.unlink(DOCKERFILE) - end - else - say_status :Dockerfile, 'Using existing Dockerfile' - end + create_tmp_file DOCKERFILE, config.to_dockerfile - if !File.exist?(SSH_CONFIG_FILE) - say_status :ssh_config, 'Creating temporary ssh config' - File.write(SSH_CONFIG_FILE, SSH_CONFIG_CONTENTS) - at_exit do - File.unlink(SSH_CONFIG_FILE) - end - else - say_status :ssh_config, 'Using existing ssh config' - end - docker :build, '-t', tag, '.' return unless config.export unless config.export_dir @@ -169,17 +146,20 @@ begin guest_export_dir = File.expand_path(config.export_dir, CIDE_SRC_DIR) host_export_dir = File.expand_path( config.host_export_dir || config.export_dir, - Dir.pwd) + Dir.pwd, + ) docker :cp, [id, guest_export_dir].join(':'), host_export_dir ensure docker :rm, '-f', id end + rescue Docker::Error => ex + exit ex.exitstatus end desc 'clean', 'Removes old containers' method_option 'days', desc: 'Number of days to keep the images', @@ -193,11 +173,11 @@ setup_docker days_to_keep = options[:days] max_images = options[:count] - x = run('docker images --no-trunc', capture: true) + x = docker('images', '--no-trunc', capture: true) iter = x.lines.each iter.next cide_image_ids = iter .map { |line| line.split(/\s+/) } .select { |line| line[0] =~ /^cide\// || line[0] == '<none>' } @@ -206,11 +186,11 @@ if cide_image_ids.empty? puts 'No images found to be cleaned' return end - x = run("docker inspect #{cide_image_ids.join(' ')}", capture: true) + x = docker('inspect', *cide_image_ids, capture: true) cide_images = JSON.parse(x.strip) .each { |image| image['Created'] = Time.iso8601(image['Created']) } .sort { |a, b| a['Created'] <=> b['Created'] } if cide_images.size > max_images @@ -226,46 +206,24 @@ if old_cide_images.empty? puts 'No images found to be cleaned' return end - run("docker rmi #{old_cide_images.join(' ')}") + docker('rmi', *old_cide_images) end desc 'init', "Creates a blank #{CONFIG_FILE} into the project" def init - if File.exist?(CONFIG_FILE) - puts "#{CONFIG_FILE} already exists" - return - end puts "Creating #{CONFIG_FILE} with default values" create_file CONFIG_FILE, DefaultConfig.to_yaml end - protected + private - def setup_docker - @setup_docker ||= ( - if `uname`.strip == 'Darwin' && !ENV['DOCKER_HOST'] - unless system('which boot2docker >/dev/null 2>&1') - puts 'make sure boot2docker is installed and running' - puts - puts '> brew install boot2docker' - exit 1 - end - - `boot2docker shellinit 2>/dev/null` - .lines - .grep(/export (\w+)=(.*)/) { ENV[$1] = $2.strip } - end - true - ) - end - - def docker(*args) - opts = args.last.is_a?(Hash) ? args.pop : {} - ret = run Shellwords.join(['docker'] + args), opts - fail 'Command failed' if $?.exitstatus > 0 - ret + def create_tmp_file(destination, *args, &block) + create_file(destination, *args, &block) + at_exit do + remove_file(destination, verbose: false) + end end end end