module DockerJockey

This has helpers for running this inside a container - docker in docker

Constants

VERSION

Public Class Methods

docker_exec(image, args, options) click to toggle source
# File lib/executor.rb, line 4
def self.docker_exec(image, args, options)
  docker_run(image, args, options)
end
docker_exec_script(image, script, options) click to toggle source
# File lib/executor.rb, line 8
def self.docker_exec_script(image, script, options)
  # TODO: change this to use docker api, like docker exec
  # write script and mount inside container
  File.write('__uberscript__', script)
  args = "chmod a+x __uberscript__ && ./__uberscript__"
  docker_run(image, args, options)
  File.delete('__uberscript__')
end
docker_host() click to toggle source
# File lib/dj.rb, line 30
def self.docker_host
  @@docker_host
end
docker_host=(x) click to toggle source
# File lib/dj.rb, line 26
def self.docker_host=(x)
  @@docker_host = x
end
docker_run(image, args, options=OpenStruct.new) click to toggle source
# File lib/executor.rb, line 17
def self.docker_run(image, args, options=OpenStruct.new)
  # mounts = DockerJockey.docker_host[]
  if !image.include? ':'
    image = image + ":latest" # was getting "No such image" error without this
  end
  DockerJockey.logger.debug("docker_exec args: #{args.inspect}")
  cmd = args.is_a?(String) ? ['sh', '-c', "#{args}"] : args
  coptions = {
    'Image' => image,
    'Cmd' => cmd,
          # "AttachStdin": true,
          # "Tty": true,
    #  "OpenStdin": true,
    #  "StdinOnce": true,
    "AttachStdout": true,
    "AttachStderr": true,
    # 'Mounts' => mounts,
    # "Env": [
            #  "FOO=bar",
            #  "BAZ=quux"
    #  ],
    'WorkingDir' => '/app',
    'HostConfig' => {},
  }
  if DockerJockey.docker_host
    # Dind version
    coptions["HostConfig"]['VolumesFrom'] = [DockerJockey.docker_host['Name']]
    coptions["HostConfig"]['Binds'] = DockerJockey.docker_host['HostConfig']['Binds']
    # 'PortBindings': DockerJockey.docker_host['HostConfig']['PortBindings'],
  else
    coptions['Volumes'] = { '/app' => {} }
    coptions["HostConfig"]['Binds'] = ["#{Dir.pwd}:/app"]
  end

  if options.env_vars && options.env_vars.length > 0
    coptions['Env'] = options.env_vars
  end
  if options.ports && options.ports.length > 0
    # info: http://stackoverflow.com/a/20429133/105562
    coptions['ExposedPorts'] = {}
    coptions['HostConfig']['PortBindings'] = {}
    options.ports.each do |p|
      psplit = p.split(':')
      coptions['ExposedPorts']["#{psplit[1]}/tcp"] = {}
      coptions['HostConfig']['PortBindings']["#{psplit[1]}/tcp"] = [{ "HostPort": "#{psplit[0]}" }]
    end
  end
  # puts "container options:"
  # p coptions
  if !Docker::Image.exist?(image)
    puts "Image #{image} doesn't exist, pulling..."
    # Pull image to make sure we have it.
    di = Docker::Image.create('fromImage' => image)
    # Temporary hack until docker gem updated: https://github.com/swipely/docker-api/issues/369
    # begin
    #   Docker::Image.create('fromImage' => image_name) do |info|
    #     yield info if block_given?
    #   end
    # rescue Docker::Error::NotFoundError => e
    #   # see: https://github.com/swipely/docker-api/issues/369
    #   # as long as this is not fixed we need to re-check on our own
    #   # on docker-engines >= 1.10
    #   if Gem::Version.new(Docker.version['Version']) >= Gem::Version.new('1.10.0')
    #     Docker::Image.get(image_name)
    #   else
    #     raise e
    #   end
    # end
  end
  # END HACK

  # Now fire it up
  container = Docker::Container.create(coptions)
  # Capture ctrl-c
  Signal.trap("INT") {
    # Ctrl-C was pressed...
    puts "Caught interrupt - killing docker child..."

    # Kill child process...
    # p `docker kill #{cname}`
    # Process.kill("INT", pid)
    container.delete(:force=>true)
    exit 0

    # This prevents the process from becoming defunct
    # oe.close
  }
  container.tap(&:start).streaming_logs(follow:true, stdout: true, stderr: true) { |stream, chunk|
    # puts "#{stream}: #{chunk}" # for debugging
    puts chunk
  }
  # puts "Deleting container"
  container.delete(:force => true)
end
exec(cmd, args = []) click to toggle source
# File lib/executor.rb, line 147
def self.exec(cmd, args = [])
  split = cmd.split(' ')
  DockerJockey.logger.debug "Exec: #{(split + args).join(' ')}"
  base = split.shift
  Open3.popen2e(base, *(split + args)) {|i,oe,t|
    pid = t.pid # pid of the started process.
    i.close # ensure this exits when it's done with output
    oe.each {|line|
      if /warning/ =~ line
        puts 'warning'
      end
      puts line
    }
    exit_status = t.value # Process::Status object returned.
    # puts "exit_status: #{exit_status}"
  }
end
find_volumes() click to toggle source

Gets volumes for parent container to be used in sub containers

# File lib/dind.rb, line 5
def self.find_volumes
  @volumes = "-v \"#{Dir.pwd}\":/app"
  begin
    puts "inspect: #{`docker inspect`}"
    di = JSON.parse("#{`docker inspect $HOSTNAME`}")
    # p di
    @volumes = "--volumes-from #{di[0]['Name']}"
    DockerJockey.docker_host = di[0]
  rescue => ex
    p ex
    puts "couldn't inspect"
  end

  if ARGV.length < 1
    DockerJockey.logger.fatal "No command provided."
    exit
  end

  # DockerJockey.logger.debug "Options: #{options.inspect}"

  DockerJockey.volumes = @volumes
end
logger() click to toggle source
# File lib/dj.rb, line 41
def self.logger
  @@logger
end
logger=(x) click to toggle source
# File lib/dj.rb, line 44
def self.logger=(x)
  @@logger = x
end
popen(maincmd, cname) click to toggle source
# File lib/executor.rb, line 112
def self.popen(maincmd, cname)
  Open3.popen2e('docker', *(maincmd)) {|i,oe,t|
    pid = t.pid # pid of the started process.
    # puts "pid: #{pid}"
    i.close # ensure this exits when it's done with output

    # Catch interrupts to shut down the docker process too
    Signal.trap("INT") {
      # Ctrl-C was pressed...
      puts "Caught interrupt - killing docker child..."

      # Kill child process...
      p `docker kill #{cname}`
      # Process.kill("INT", pid)

      # This prevents the process from becoming defunct
      # oe.close
    }

    begin
      oe.each {|line|
        if /warning/ =~ line
          puts 'warning'
        end
        puts line
      }
    rescue IOError => ex
      # ignore?  closed stream probably
    end
    oe.close
    exit_status = t.value # Process::Status object returned.
    # puts "exit_status: #{exit_status}"
  }
end
run(args, options) click to toggle source
# File lib/dj.rb, line 48
def self.run(args, options)
  begin
    lang = args.shift
    case lang
    when 'git'
      helper = DockerJockey::GitHelper.new
      helper.run(args, options)
    when 'go'
      helper = DockerJockey::GoHelper.new
      helper.run(args, options)
    when 'ruby'
      helper = DockerJockey::RubyHelper.new
      helper.run(args, options)
    when 'node'
      helper = DockerJockey::NodeHelper.new
      helper.run(args, options)
    when 'python'
      helper = DockerJockey::PythonHelper.new
      helper.run(args, options)
    when 'php'
      helper = DockerJockey::PhpHelper.new
      helper.run(args, options)
    else
      puts "ERROR: Language not supported: #{lang}"
    end
  # rescue SystemExit, Interrupt => ex
    # puts "Caught #{ex}"
  end
end
volumes() click to toggle source
# File lib/dj.rb, line 38
def self.volumes
  @@volumes
end
volumes=(x) click to toggle source
# File lib/dj.rb, line 34
def self.volumes=(x)
  @@volumes = x
end