class Fidoci::Env

Environment configuration of D encapsulates container image building and running commands in it

Attributes

env_config[RW]

Public Class Methods

new(image, name, env_config) click to toggle source
# File lib/fidoci/env.rb, line 9
def initialize(image, name, env_config)
    @name = name.to_s
    @image = image
    @env_config = env_config
end

Public Instance Methods

build_image() click to toggle source
# File lib/fidoci/env.rb, line 23
def build_image
    image = Docker::Image.get(image_name) rescue nil
    unless image
        image = do_build_image
    end

    image
end
clean!() click to toggle source

clean images and containers associated with this Env

# File lib/fidoci/env.rb, line 148
def clean!
    clean_containers! rescue nil
    clean_image! rescue nil
end
clean_container(cname) click to toggle source
# File lib/fidoci/env.rb, line 122
def clean_container(cname)
    begin
        debug "Cleaning container #{cname}..."
        container = Docker::Container.get(cname)
        container.remove(force: true, v: true)
    rescue
        debug "Cleaning failed"
        nil
    end
end
clean_containers!() click to toggle source
# File lib/fidoci/env.rb, line 137
def clean_containers!
    containers = [container_name]
    containers += links.map{|key, config| link_container_name(key) }

    containers.each{|cname|
        clean_container(cname)
    }
    true
end
clean_image!() click to toggle source
# File lib/fidoci/env.rb, line 112
def clean_image!
    begin
        debug "Cleaning image #{image_name}"
        image = Docker::Image.get(image_name)
        image.remove(force: true)
    rescue
        true
    end
end
cmd(*args) click to toggle source

run command in docker, building the image and starting services first if needed

# File lib/fidoci/env.rb, line 174
def cmd(*args)
    if build_image && link_containers
        debug "Running `#{args.join(' ')}`..."
        docker_run_or_exec(*args)
    else
        debug "Build failed"
        return false
    end
end
commands() click to toggle source
# File lib/fidoci/env.rb, line 364
def commands
    return false unless env_config['commands']

    success = env_config['commands'].all? { |command|
        info "Running `#{command}`..."

        unless command.is_a? Array
            command = command.split(/\s+/)
        end

        state = cmd(*command)
        info "Exited with state #{state}"

        state == 0
    }

    success
end
config_params(params, config) click to toggle source
# File lib/fidoci/env.rb, line 311
def config_params(params, config)
    params['HostConfig'] ||= {}

    # env
    if config['environment']
        config['environment'].each {|key,val|
            params['Env'] ||= []
            params['Env'] << "#{key}=#{val}"
        }
    end

    if config['environment_pass']
        config['environment_pass'].each{|key|
            params['Env'] ||= []
            params['Env'] << "#{key}=#{ENV[key]}"

            puts "Warning: Passing environment variable #{key} is not locally set" unless ENV[key]
        }
    end

    # volumes
    if config['volumes']
        config['volumes'].each {|v|
            params['HostConfig']['Binds'] ||= []

            host_path, container_path = v.split(':')
            host_path = File.expand_path(host_path)

            params['HostConfig']['Binds'] << "#{host_path}:#{container_path}"
        }
    end

    # ports
    if config['ports']
        config['ports'].each {|p|
            parts = p.split(':')
            container_port = parts.last
            host_port = parts[-2]
            host_ip = parts[-3] || ""

            params['HostConfig']['PortBindings'] ||= {}
            params['HostConfig']['PortBindings']["#{container_port}/tcp"] = [
                {
                    "HostIp" => host_ip,
                    "HostPort" => host_port
                }
            ]
        }
    end

    params
end
container_name() click to toggle source
# File lib/fidoci/env.rb, line 108
def container_name
    @image.gsub(/[^a-zA-Z0-9_]/, '_') + "_" + @name.gsub(/[^a-zA-Z0-9_]/, '_')
end
debug(msg) click to toggle source
# File lib/fidoci/env.rb, line 15
def debug(msg)
    puts msg if ENV['DEBUG']
end
do_build_image() click to toggle source
# File lib/fidoci/env.rb, line 32
def do_build_image
    info "Building image #{image_name}..."
    params = {}

    params['forcerm'] = true
    params['t'] = image_name
    if env_config['dockerfile']
        params['dockerfile'] = env_config['dockerfile']
    end

    last_intermediate_image = nil
    image = Docker::Image.build_from_dir('.', params) do |chunk|
        json = JSON.parse(chunk)
        if (json['stream'] =~ /\ --->\ ([a-f0-9]{12})/) == 0
            last_intermediate_image = $1
        end
        $stdout << json['stream']
    end
    last_intermediate_image = nil

    image
ensure
    begin
        if last_intermediate_image
            img = Docker::Image.get(last_intermediate_image)
            img.json
            puts "Removing intermediate image #{last_intermediate_image}"
            img.remove('force' => true)
        end
    rescue
        nil
    end
end
docker_exec(id, *args) click to toggle source
# File lib/fidoci/env.rb, line 239
def docker_exec(id, *args)
    params = {}
    params["Container"] = id
    params["AttachStdin"] = true
    params["AttachStdout"] = true
    params["AttachStderr"] = true
    params["Tty"] = true
    params["Cmd"] = args

    docker_exec = Docker::Exec.create(params)
    result = nil

    $stdin.raw do
        result = docker_exec.start!(tty: true, stdin: $stdin) do |msg|
            $stdout << msg
        end
    end

    debug "Exited with status #{result[2]}"

    result[2]
end
docker_run(*args) click to toggle source

calls docker run command with all needed parameters attaches stdin and stdout

# File lib/fidoci/env.rb, line 264
def docker_run(*args)
    params = {}
    params['AttachStdin'] = true
    params['OpenStdin'] = true
    params['Tty'] = true
    params['name'] = container_name

    # links
    link_containers.each{|name, container_name|
        params['HostConfig'] ||= {}
        params['HostConfig']['Links'] ||= []
        params['HostConfig']['Links'] << "#{container_name}:#{name}"
    }

    config_params(params, env_config)
    params['Image'] = image_name
    params['Cmd'] = args

    #puts params

    container = Docker::Container.create(params)

    receiver = ->(msg) {
        $stdout << msg
        $stdout.flush
    }

    if $stdin && $stdin.tty?

        $stdin.raw do
            container.start!
            lines, cols = IO.console.winsize rescue [60,80]
            container.connection.post("/containers/#{container.id}/resize", h: lines, w: cols)
            container.attach(tty: true, stdin: $stdin, &receiver)
        end
    else
        container.start!.attach(tty: true, &receiver)
    end

    status = container.json['State']['ExitCode']
    debug "Exited with status #{status}"

    status
ensure
    clean_container(container_name)
end
docker_run_or_exec(*args) click to toggle source
# File lib/fidoci/env.rb, line 229
def docker_run_or_exec(*args)
    container = Docker::Container.get(container_name) rescue nil

    if container
        docker_exec(container.id, *args)
    else
        docker_run(*args)
    end
end
image_name() click to toggle source
# File lib/fidoci/env.rb, line 66
def image_name
    "%s:%s" % [@image, @name]
end
info(msg) click to toggle source
# File lib/fidoci/env.rb, line 19
def info(msg)
    puts msg
end
push(tag) click to toggle source
# File lib/fidoci/env.rb, line 77
def push(tag)
    repo_tag = "#{@image}:#{tag}"
    image = Docker::Image.get(repo_tag)

    if ENV['DOCKER_AUTH_FILE']
      # load file
      auth_file = JSON.parse(File.read ENV['DOCKER_AUTH_FILE'])
      image_parts = @image.split('/')
      registry = image_parts.size == 2 ? "https://#{image_parts[0]}" : "https://index.docker.io/v1/"
      creds = auth_file['auths'][registry]
      if creds['auth']
        creds['username'], creds['password'] = Base64.decode64(creds['auth']).split(':', 2)
        creds.delete 'auth'
      end
    else
      creds = {
        'username' => ENV['DOCKER_REGISTRY_USERNAME'],
        'password' => ENV['DOCKER_REGISTRY_PASSWORD'],
        'email' => ENV['DOCKER_REGISTRY_EMAIL']
      }
    end
   
    info "Pushing #{repo_tag}..."
    image.push(creds, 'repo_tag' => repo_tag) do |msg|
        json = JSON.parse(msg)
        $stdout.puts json['status']
        $stdout.puts json['progress'] if json['progress']
        $stdout.puts "Error: #{json['error']}" if json['error']
    end
end
stop!() click to toggle source

stop services and clean main container

# File lib/fidoci/env.rb, line 154
def stop!
    links.map{|key, config|
        cname = link_container_name(key)
        if links[key]['shared']
            debug "Keeping running #{cname}..."
            next
        end

        begin
            debug "Stopping container #{cname}..."
            container = Docker::Container.get(cname)
            container.stop!
        rescue
            nil
        end
    }
end
tag_image(tag) click to toggle source
# File lib/fidoci/env.rb, line 70
def tag_image(tag)
    info "Tagging images as #{@image}:#{tag}..."

    image = Docker::Image.get(image_name)
    image.tag('repo' => @image, 'tag' => tag, 'force' => true)
end