class DockerSpec

Documentation

Constants

CONFIG_FILE
CONTAINER_RUN_WAIT_TIMEOUT
DOCKER_AUTH_FILE
ROOT_DIR

Attributes

config[RW]
container[RW]
test_failed[RW]

Public Instance Methods

build_docker_image() click to toggle source
# File lib/docker/spec.rb, line 153
def build_docker_image
  puts
  # Rebuild the cache filesystem
  build_args = ''
  build_args += ' --no-cache' if @config[:clear_cache]

  # Build the docker image
  build_cmd = "docker build -t #{@config[:image_name]} #{build_args} ."
  status = POpen4.popen4(build_cmd) do |stdout, stderr, _stdin|
    stdout.each { |line| puts line }
    stderr.each { |line| puts line.red }
  end
  if status.exitstatus != 0
    puts("ERROR: #{build_cmd} failed")
    exit 1
  end
end
build_root() click to toggle source
# File lib/docker/spec.rb, line 132
  def build_root
    command = <<EOF
bash -ec '
  export WD="$(pwd)"
  export TMPDIR=$(mktemp -d -t docker-spec.XXXXXX)
  
  cp -r root $TMPDIR/root
  cd $TMPDIR
  sudo chown root:root -R root
  cd root 
  sudo tar --mtime="1970-01-01" -c -f ../root.tar .
  cd ../
  sudo chown -R `id -u`:`id -g` root.tar
  cp root.tar "${WD}"
  touch -t 200001010000.00 "${WD}/root.tar"
  sudo rm -rf $TMPDIR
'
EOF
    system command if Dir.exist?(ROOT_DIR)
  end
clean_up() click to toggle source
# File lib/docker/spec.rb, line 231
def clean_up
  # Keep container running
  if @config[:keep_running] && @config[:standard_tests]
    puts "\nINFO: To connect to a running container: \n\n" \
      "docker exec -ti #{@container.info["Name"][1..-1]} bash"
  else
    delete_container
  end
end
delete_container() click to toggle source
# File lib/docker/spec.rb, line 226
def delete_container
  @container.kill
  @container.delete
end
get_config(key, envvar, question, default = nil) click to toggle source
# File lib/docker/spec.rb, line 241
def get_config(key, envvar, question, default = nil)
  value = ENV[envvar]
  value = true if value.class == String && value.match(/^(true|yes|y)$/i)
  value = false if value.class == String && value.match(/^(false|no|n)$/i)
  value = @config[key] if value.nil?
  if value.nil?
    value = default.nil? ? agree(question, 'n') : default
  end
  value
end
grab_flock() click to toggle source
# File lib/docker/spec.rb, line 40
def grab_flock
  @config = DockerSpec.instance.config
  @lock = File.join('/tmp', @config[:account] + '-' + @config[:name].gsub('/', '-') + '.lock')
  @f = File.open(@lock, 'w')
  if (not @f.flock(File::LOCK_EX | File::LOCK_NB))
    puts "INFO: Another build is already running #{@lock}"
  end
  @f.flock(File::LOCK_EX)
end
load_config() click to toggle source
# File lib/docker/spec.rb, line 108
def load_config
  File.exist?(CONFIG_FILE) || fail('Could not load docker_spec.yml')
  @config = YAML.load(File.read(CONFIG_FILE)) ||
            fail('docker_spec.yml is not a valid yml file')

  @config[:name] || fail('name is not defined in docker_spec.yml')
  @config[:account] || fail('account is not defined in docker_spec.yml')
  @config[:image_name] = format '%s/%s', @config[:account], @config[:name]
  @config[:build_image] = get_config(:build_image, 'DOCKER_SPEC_BUILD_DOCKER_IMAGE',
                                     'Build docker image? ')
  @config[:build_root] = get_config(:build_root, 'DOCKER_SPEC_BUILD_ROOT',
                                    'Rebuild root filesystem? ') if @config[:build_image]
  @config[:clear_cache] = get_config(:clear_cache, 'DOCKER_SPEC_CLEAR_CACHE',
                                     'Clear docker cache? ') if @config[:build_image]
  @config[:tag_db] = get_config(:tag_db, 'DOCKER_SPEC_TAG_DB', 'tag db?')
  @config[:tag_prefix] = get_config(:tag_prefix, 'DOCKER_SPEC_TAG_PREFIX', 'tag prefix?', '')
  @config[:standard_tests] = get_config(:standard_tests, 'DOCKER_SPEC_STANDARD_TESTS', '', true)
  @config[:keep_running] = get_config(:keep_running, 'DOCKER_SPEC_KEEP_RUNNING',
                                                 "\nKeep container running? ")
  @config[:push_container] = get_config(:push_container, 'DOCKER_SPEC_PUSH_CONTAINER',
                                        'Push new tag? ')
  @config
end
push() click to toggle source
# File lib/docker/spec.rb, line 50
def push
  if @config[:push_container]

    @config[:tag_db] ||
      fail('tag_db is not defined in docker_spec.yml')

    # Load credentials from config file, or default docker config
    if @config[:registry]
      @config[:registry][:username] ||
        fail('registry->username is not defined in docker_spec.yml')
      @config[:registry][:password] ||
        fail('registry->password is not defined in docker_spec.yml')
      @config[:registry][:email] ||
        fail('registry->email is not defined in docker_spec.yml')
    else
      auth = @config[:auth] || 'https://index.docker.io/v1/'
      @config[:registry] = Hash.new
      docker_auth = JSON.parse(File.read(File.expand_path(DOCKER_AUTH_FILE)))
      auth_base64 = docker_auth['auths'][auth]['auth']
      @config[:registry][:username] = Base64.decode64(auth_base64).split(':').first
      @config[:registry][:password] = Base64.decode64(auth_base64).split(':').last
      @config[:registry][:email] = docker_auth['auths'][auth]['email']
    end

    # Open key value store and get the current tag for this repo
    tag_prefix = @config[:tag_prefix]
    store = Moneta.new(:YAML, file: File.expand_path(@config[:tag_db]))
    if  store.key?(@config[:image_name]) && !store[@config[:image_name]].to_s.match(/#{tag_prefix}(.*)/).nil?
      current_tag = store[@config[:image_name]].to_s.match(/#{tag_prefix}(.*)/)[1].to_i 
    else 
      current_tag = 0
    end
    new_tag = tag_prefix + (current_tag + 1).to_s

    image = Docker::Image.all.detect do |i|
      i.info['RepoTags'].include?(@config[:image_name] + ':latest')
    end

    # Login to docker hub and push latest and new_tag
    Docker.authenticate! username: @config[:registry][:username],
                         password: @config[:registry][:password],
                         email: @config[:registry][:email],
                         serveraddress: @config[:serveraddress] || 'https://index.docker.io'

    image.tag repo: @config[:image_name], tag: new_tag, force: true
    puts "\nINFO: pushing #{@config[:image_name]}:#{new_tag} to Registry"
    image.push nil, tag: new_tag

    image.tag repo: @config[:image_name], tag: 'latest', force: true
    puts "INFO: pushing #{@config[:image_name]}:latest to Registry"
    image.push nil, tag: 'latest'

    # Store the new tag in the tag_db
    store[@config[:image_name]] = new_tag.to_s
    store.close
  end
end
rspec_configure() click to toggle source
# File lib/docker/spec.rb, line 171
def rspec_configure
  set :backend, :docker
  $docker_spec_config = @config

  RSpec.configure do |rc|
    rc.fail_fast = true

    rc.after(:each) do |test|
      DockerSpec.instance.test_failed = true if test.exception
    end

    rc.before(:suite) do
      DockerSpec.instance.start_container
    end

    rc.after(:suite) do
      DockerSpec.instance.clean_up
      DockerSpec.instance.push unless DockerSpec.instance.test_failed
    end
  end
  Docker::Spec::docker_tests
end
run() click to toggle source
# File lib/docker/spec.rb, line 29
def run
  @config = nil
  @test_failed = false

  load_config
  grab_flock if @config[:lock]
  build_root if @config[:build_root]
  build_docker_image if @config[:build_image]
  rspec_configure
end
start_container() click to toggle source
# File lib/docker/spec.rb, line 194
def start_container
  # Run  the container with options
  opts = {}
  opts['HostConfig'] = { 'NetworkMode' => @config[:network_mode] } \
    unless @config[:network_mode].nil?

  opts['env'] = @config[:env] unless @config[:env].nil?
  opts['Image'] = @config[:image_name]

  @container = Docker::Container.create(opts).start

  Timeout::timeout(10) do
    loop do
      @container.refresh!
      break if @container.info["State"]["Running"]
    end
  end if @config[:standard_tests]


  # Check the logs, when it stops logging we can assume the container
  # is fully up and running
  log_size_ary = []
  CONTAINER_RUN_WAIT_TIMEOUT.times do
    log_size_ary << @container.logs(stdout: true).size
    break if log_size_ary.last(3).sort.uniq.size == 1 &&
             log_size_ary.last(3).sort.uniq.last > 0
    sleep 1
  end if @config[:standard_tests]

  set :docker_container, @container.id
end
to_boolean(str) click to toggle source
# File lib/docker/spec.rb, line 252
def to_boolean(str)
  str == 'true' unless str.nil?
end