class Linecook::AmiPacker

Constants

BUILDER_CONFIG
CHROOT_COMMANDS
PACKER_PATH
PACKER_VERSION
POST_MOUNT_COMMANDS
PRE_MOUNT_COMMANDS
ROOT_DEVICE_MAP
SOURCE_URL

Public Class Methods

new(config) click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 70
def initialize(config)
  system("#{packer_path} --version")
  @hvm = config[:hvm] || true
  @root_size = config[:root_size] || 10
  @region = config[:region] || 'us-east-1'
  @copy_regions = config[:copy_regions] || []
  @accounts = config[:account_ids] || []
  @write_txt = Linecook.config[:packager] && Linecook.config[:packager][:ami] && Linecook.config[:packager][:ami][:update_txt]
end

Public Instance Methods

package(image, directory) click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 80
def package(image, directory)
  @image = image
  kitchen_config = load_config(directory).send(:data).instance_variable_get(:@data)
  image_config = kitchen_config[:suites].find{ |x| x[:name] == image.name }
  if image_config && image_config[:packager]
    packager = image_config[:packager] || {}
  end
  conf_file = Tempfile.new("#{@image.id}-packer.json")
  config = generate_config(packager)
  conf_file.write(config)
  conf_file.close
  output = []
  PTY.spawn("sudo #{PACKER_PATH} build -machine-readable #{conf_file.path}") do |stdout, _, _|
    begin
      stdout.each do |line|
        output << line if line =~ /artifact/
        tokens = line.split(',')
        if tokens.length > 4
          out = tokens[4].gsub('%!(PACKER_COMMA)', ',')
          time = DateTime.strptime(tokens[0], '%s').strftime('%c')
          puts "#{time} | #{out}"
        else
          puts "unexpected output format"
          puts tokens
        end
      end
    rescue Errno::EIO
      puts "Packer finshed executing"
    end
  end
  extract_amis_from_output(output)
ensure
  conf_file.close
  conf_file.unlink
end

Private Instance Methods

build_provisioner(chroot_commands) click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 150
def build_provisioner(chroot_commands)
  provisioner = [
    {
      type: 'shell',
      inline: chroot_commands
    }
  ]
end
extract_amis_from_output(output) click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 182
def extract_amis_from_output(output)
  amis = output.grep(/amazon-chroot,artifact,0,id/).first.chomp.split(',')[5].split('%!(PACKER_COMMA)')
  amis.each do |info_str|
    ami_info = info_str.split(':')
    ami_region = ami_info[0]
    ami_id = ami_info[1]
    puts "Built #{ami_id} for #{ami_region}"
    Linecook::Route53.upsert_record(@image.name, ami_id, ami_region) if @write_txt
  end
end
generate_config(packager) click to toggle source

TO DO: support for multiple accounts, multiple regions code to extract ami name(s) from output

amis = `grep "amazon-ebs,artifact,0,id" packer.log`.chomp.split(',')[5].split('%!(PACKER_COMMA)')

route53 TXT record integration

# File lib/linecook-gem/packager/packer.rb, line 124
def generate_config(packager)
  packager ||= {}
  config = {
    variables: {
      aws_access_key: Linecook.config[:aws][:access_key],
      aws_secret_key: Linecook.config[:aws][:secret_key],
      image_name: "linecook-#{@image.id}",
      source_image_path: @image.path
    },
    builders: [
      BUILDER_CONFIG.merge(
        ami_users: @accounts,
        ami_regions: @copy_regions,
        ami_virtualization_type: virt_type,
        root_volume_size: @root_size
      ).deep_merge(packager[:builder] || {})
    ],
    provisioners: build_provisioner(CHROOT_COMMANDS)
  }

  unless config[:builders].first[:ami_block_device_mappings].find { |x| x[:device_name] == ROOT_DEVICE_MAP[:device_name] }
    config[:builders].first[:ami_block_device_mappings].prepend ROOT_DEVICE_MAP
  end
  JSON.pretty_generate(config)
end
get_packer() click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 170
def get_packer
  puts "packer too old (<#{PACKER_VERSION}) or not present, getting latest packer"
  arch = 1.size == 8 ? 'amd64' : '386'

  FileUtils.rm_f(Dir[File.join(File.dirname(PACKER_PATH), "*")])
  path = File.join(File.dirname(PACKER_PATH), 'packer.zip')
  url = File.join(SOURCE_URL, PACKER_VERSION, "packer_#{PACKER_VERSION}_linux_#{arch}.zip")
  download(url, path)
  unzip(path)
  PACKER_PATH
end
packer_path() click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 159
def packer_path
  @path ||= begin
    found = File.exists?(PACKER_PATH) ? PACKER_PATH : find_executable('packer')
    path = if found
      version = `#{found} --version`
      Gem::Version.new(version) >= Gem::Version.new(PACKER_VERSION) ? found : nil
    end
    path ||= get_packer
  end
end
virt_type() click to toggle source
# File lib/linecook-gem/packager/packer.rb, line 193
def virt_type
  @hvm ? 'hvm' : 'paravirtual'
end