linecook-gem 0.6.10 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/linecook-gem.rb +4 -6
- data/lib/linecook-gem/baker.rb +110 -0
- data/lib/linecook-gem/baker/docker.rb +114 -0
- data/lib/linecook-gem/cli.rb +63 -115
- data/lib/linecook-gem/config.rb +45 -0
- data/lib/linecook-gem/image.rb +77 -0
- data/lib/linecook-gem/image/crypt.rb +7 -40
- data/lib/linecook-gem/image/s3.rb +14 -14
- data/lib/linecook-gem/packager.rb +30 -0
- data/lib/linecook-gem/packager/packer.rb +249 -0
- data/lib/linecook-gem/packager/route53.rb +62 -0
- data/lib/linecook-gem/packager/squashfs.rb +51 -0
- data/lib/linecook-gem/util/downloader.rb +3 -42
- data/lib/linecook-gem/util/secrets.rb +47 -0
- data/lib/linecook-gem/version.rb +1 -1
- data/man/LINECOOK.1 +117 -86
- metadata +62 -110
- data/lib/linecook-gem/builder/build.rb +0 -44
- data/lib/linecook-gem/builder/darwin_backend.rb +0 -98
- data/lib/linecook-gem/builder/linux_backend.rb +0 -11
- data/lib/linecook-gem/builder/lxc.rb +0 -286
- data/lib/linecook-gem/builder/manager.rb +0 -79
- data/lib/linecook-gem/image/github.rb +0 -27
- data/lib/linecook-gem/image/manager.rb +0 -73
- data/lib/linecook-gem/packager/ebs.rb +0 -373
- data/lib/linecook-gem/packager/manager.rb +0 -23
- data/lib/linecook-gem/provisioner/chef-zero.rb +0 -149
- data/lib/linecook-gem/provisioner/manager.rb +0 -47
- data/lib/linecook-gem/provisioner/packer.rb +0 -82
- data/lib/linecook-gem/util/config.rb +0 -134
- data/lib/linecook-gem/util/executor.rb +0 -33
- data/lib/linecook-gem/util/ssh.rb +0 -190
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Linecook
|
5
|
+
def self.config
|
6
|
+
config = Config.config
|
7
|
+
config
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
module Config
|
12
|
+
extend self
|
13
|
+
|
14
|
+
CONFIG_PATH = File.join(Dir.pwd, 'linecook.yml').freeze
|
15
|
+
SECRETS_PATH = File.join(Dir.pwd, 'secrets.ejson').freeze
|
16
|
+
LINECOOK_HOME = File.expand_path('~/.linecook').freeze
|
17
|
+
DEFAULT_CONFIG_PATH = File.join(LINECOOK_HOME, 'config.yml').freeze
|
18
|
+
|
19
|
+
def config
|
20
|
+
@config ||= begin
|
21
|
+
config_path = ENV['LINECOOK_CONFIG_PATH'] || CONFIG_PATH
|
22
|
+
config = {}
|
23
|
+
config ||= YAML.load(File.read(DEFAULT_CONFIG_PATH)) if File.exist?(DEFAULT_CONFIG_PATH)
|
24
|
+
config.deep_merge!(YAML.load(File.read(config_path))) if File.exist?(config_path)
|
25
|
+
(config || {}).deep_symbolize_keys!
|
26
|
+
config.deep_merge!(secrets)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def secrets
|
32
|
+
@secrets ||= begin
|
33
|
+
secrets_path = ENV['LINECOOK_SECRETS_PATH'] || SECRETS_PATH
|
34
|
+
if File.exists?(secrets_path)
|
35
|
+
ejson_path = File.join(Gem::Specification.find_by_name('ejson').gem_dir, 'build', 'linux-amd64', 'ejson' )
|
36
|
+
command = "#{ejson_path} decrypt #{secrets_path}"
|
37
|
+
secrets = JSON.load(`sudo #{command}`)
|
38
|
+
secrets.deep_symbolize_keys
|
39
|
+
else
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
require 'linecook-gem/image/s3'
|
5
|
+
require 'linecook-gem/image/crypt'
|
6
|
+
require 'linecook-gem/util/downloader'
|
7
|
+
|
8
|
+
module Linecook
|
9
|
+
class Image
|
10
|
+
|
11
|
+
include Downloader
|
12
|
+
include Crypto
|
13
|
+
|
14
|
+
IMAGE_PATH = File.join(Config::LINECOOK_HOME, 'images').freeze
|
15
|
+
|
16
|
+
attr_reader :path, :id, :name, :group, :tag
|
17
|
+
|
18
|
+
|
19
|
+
def initialize(name, group, tag)
|
20
|
+
@name = name
|
21
|
+
@group = group ? "#{name}-#{group.gsub(/-|\//,'_')}" : name
|
22
|
+
@tag = tag
|
23
|
+
@id = if @tag == 'latest'
|
24
|
+
id = File.basename(latest).split('.')[0]
|
25
|
+
@tag = id.split('-').last
|
26
|
+
id
|
27
|
+
elsif tag
|
28
|
+
"#{@group}-#{@tag}"
|
29
|
+
else
|
30
|
+
@group
|
31
|
+
end
|
32
|
+
@path = image_path
|
33
|
+
end
|
34
|
+
|
35
|
+
def fetch
|
36
|
+
return if File.exists?(@path)
|
37
|
+
|
38
|
+
Dir.mktmpdir("#{@id}-download") do |tmpdir|
|
39
|
+
tmppath = File.join(tmpdir, File.basename(@path))
|
40
|
+
download(url, tmppath)
|
41
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
42
|
+
decrypt(tmppath, dest: @path)
|
43
|
+
FileUtils.rm_f(tmppath)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def upload
|
49
|
+
puts "Encrypting and uploading image #{@path}"
|
50
|
+
encrypted = encrypt(@path)
|
51
|
+
provider.upload(encrypted, group: @group)
|
52
|
+
FileUtils.rm_f(encrypted)
|
53
|
+
end
|
54
|
+
|
55
|
+
def url
|
56
|
+
provider.url(@id, group: @group)
|
57
|
+
end
|
58
|
+
|
59
|
+
def list
|
60
|
+
provider.list(group: @group)
|
61
|
+
end
|
62
|
+
|
63
|
+
def latest
|
64
|
+
provider.latest(@group)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def image_path
|
70
|
+
File.join([IMAGE_PATH, @group, "#{@id}.tar.xz"].compact)
|
71
|
+
end
|
72
|
+
|
73
|
+
def provider
|
74
|
+
S3Manager
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -1,60 +1,27 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
|
3
1
|
require 'rbnacl/libsodium'
|
4
2
|
|
5
|
-
require 'linecook-gem/image/manager'
|
6
|
-
require 'linecook-gem/util/executor'
|
7
|
-
require 'linecook-gem/util/config'
|
8
|
-
|
9
3
|
module Linecook
|
10
|
-
|
11
|
-
include Executor
|
12
|
-
|
13
|
-
def initialize(remote: nil)
|
14
|
-
@remote = remote
|
15
|
-
load_key
|
16
|
-
end
|
4
|
+
module Crypto
|
17
5
|
|
18
|
-
def
|
19
|
-
|
20
|
-
encrypt_file(image_path)
|
6
|
+
def self.keygen
|
7
|
+
RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes).unpack('H*').first
|
21
8
|
end
|
22
9
|
|
23
|
-
def
|
10
|
+
def encrypt(source, dest: nil)
|
24
11
|
dest ||= "/tmp/#{File.basename(source)}"
|
25
12
|
File.write(dest, box.encrypt(IO.binread(source)))
|
26
13
|
dest
|
27
14
|
end
|
28
15
|
|
29
|
-
def
|
16
|
+
def decrypt(source, dest: nil)
|
30
17
|
dest ||= "/tmp/#{File.basename(source)}-decrypted"
|
31
|
-
|
32
|
-
script = "/tmp/decrypt-#{SecureRandom.hex(4)}"
|
33
|
-
@remote.upload(decryptor_script(source, dest), script)
|
34
|
-
@remote.run("bash #{script}")
|
35
|
-
@remote.run("rm #{script}")
|
36
|
-
else
|
37
|
-
File.write(dest, box.decrypt(IO.binread(source)))
|
38
|
-
end
|
18
|
+
File.write(dest, box.decrypt(IO.binread(source)))
|
39
19
|
dest
|
40
20
|
end
|
41
21
|
|
42
|
-
def self.keygen
|
43
|
-
RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes).unpack('H*').first
|
44
|
-
end
|
45
|
-
|
46
22
|
private
|
47
|
-
|
48
|
-
def decryptor_script(source, dest)
|
49
|
-
"ruby -e \"require 'rbnacl/libsodium'; box = RbNaCl::SimpleBox.from_secret_key(['#{@secret_key}'].pack('H*')); File.write('#{dest}', box.decrypt(File.read('#{source}')))\""
|
50
|
-
end
|
51
|
-
|
52
23
|
def box
|
53
|
-
@box ||= RbNaCl::SimpleBox.from_secret_key([
|
54
|
-
end
|
55
|
-
|
56
|
-
def load_key
|
57
|
-
@secret_key = Linecook.config[:imagekey]
|
24
|
+
@box ||= RbNaCl::SimpleBox.from_secret_key([Linecook.config[:imagekey]].pack('H*'))
|
58
25
|
end
|
59
26
|
end
|
60
27
|
end
|
@@ -5,29 +5,29 @@ module Linecook
|
|
5
5
|
module S3Manager
|
6
6
|
extend self
|
7
7
|
EXPIRY = 20
|
8
|
-
PREFIX = '
|
8
|
+
PREFIX = 'built-images'
|
9
9
|
|
10
|
-
def url(
|
10
|
+
def url(id, group: nil)
|
11
11
|
client
|
12
12
|
s3 = Aws::S3::Resource.new
|
13
|
-
obj = s3.bucket(Linecook.config[:aws][:s3][:bucket]).object(File.join([PREFIX,
|
13
|
+
obj = s3.bucket(Linecook.config[:aws][:s3][:bucket]).object(File.join([PREFIX, group, "#{id}.tar.xz"].compact))
|
14
14
|
obj.presigned_url(:get, expires_in: EXPIRY * 60)
|
15
15
|
end
|
16
16
|
|
17
|
-
def list(
|
18
|
-
list_objects(
|
17
|
+
def list(group: nil)
|
18
|
+
list_objects(group: group).map{ |x| x.key if x.key =~ /\.tar\.xz/ }.compact
|
19
19
|
end
|
20
20
|
|
21
|
-
def latest(
|
22
|
-
objects = list_objects(
|
21
|
+
def latest(group)
|
22
|
+
objects = list_objects(group: group).sort! { |a,b| a.last_modified <=> b.last_modified }
|
23
23
|
key = objects.last ? objects.last.key : nil
|
24
24
|
end
|
25
25
|
|
26
|
-
def upload(path,
|
26
|
+
def upload(path, group: nil)
|
27
27
|
File.open(path, 'rb') do |file|
|
28
|
-
|
29
|
-
pbar = ProgressBar.create(title:
|
30
|
-
common_opts = { bucket: Linecook.config[:aws][:s3][:bucket], key: File.join([PREFIX,
|
28
|
+
fid = File.basename(path)
|
29
|
+
pbar = ProgressBar.create(title: fid, total: file.size)
|
30
|
+
common_opts = { bucket: Linecook.config[:aws][:s3][:bucket], key: File.join([PREFIX, group, fid].compact) }
|
31
31
|
resp = client.create_multipart_upload(storage_class: 'REDUCED_REDUNDANCY', server_side_encryption: 'AES256', **common_opts)
|
32
32
|
id = resp.upload_id
|
33
33
|
part = 0
|
@@ -39,7 +39,7 @@ module Linecook
|
|
39
39
|
parts << { etag: resp.etag, part_number: part }
|
40
40
|
total += content.length
|
41
41
|
pbar.progress = total
|
42
|
-
pbar.title = "#{
|
42
|
+
pbar.title = "#{fid} - (#{((total.to_f/file.size.to_f)*100.0).round(2)}%)"
|
43
43
|
end
|
44
44
|
client.complete_multipart_upload(upload_id: id, multipart_upload: { parts: parts }, **common_opts)
|
45
45
|
end
|
@@ -47,8 +47,8 @@ module Linecook
|
|
47
47
|
|
48
48
|
private
|
49
49
|
|
50
|
-
def list_objects(
|
51
|
-
client.list_objects(bucket: Linecook.config[:aws][:s3][:bucket], prefix: File.join([PREFIX,
|
50
|
+
def list_objects(group: nil)
|
51
|
+
client.list_objects(bucket: Linecook.config[:aws][:s3][:bucket], prefix: File.join([PREFIX, group].compact)).contents
|
52
52
|
end
|
53
53
|
|
54
54
|
def client
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
require 'linecook-gem/image'
|
3
|
+
require 'linecook-gem/packager/packer'
|
4
|
+
require 'linecook-gem/packager/squashfs'
|
5
|
+
require 'kitchen/configurable'
|
6
|
+
|
7
|
+
|
8
|
+
module Linecook
|
9
|
+
module Packager
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def package(image, name: 'packer')
|
13
|
+
image.fetch
|
14
|
+
provider(name.to_sym).package(image)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def provider(name)
|
19
|
+
config = Linecook.config[:packager][name]
|
20
|
+
case name
|
21
|
+
when :packer
|
22
|
+
Linecook::AmiPacker.new(**config)
|
23
|
+
when :squashfs
|
24
|
+
Linecook::Squashfs.new(**config)
|
25
|
+
else
|
26
|
+
fail "No packager implemented for for #{name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'mkmf'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'pty'
|
8
|
+
|
9
|
+
require 'linecook-gem/image'
|
10
|
+
require 'linecook-gem/util/downloader'
|
11
|
+
require 'linecook-gem/packager/route53'
|
12
|
+
|
13
|
+
module Linecook
|
14
|
+
class AmiPacker
|
15
|
+
|
16
|
+
include Downloader
|
17
|
+
|
18
|
+
SOURCE_URL = 'https://releases.hashicorp.com/packer/'
|
19
|
+
PACKER_VERSION = '0.8.6'
|
20
|
+
PACKER_PATH = File.join(Linecook::Config::LINECOOK_HOME, 'bin', 'packer')
|
21
|
+
|
22
|
+
BUILDER_CONFIG = {
|
23
|
+
type: 'amazon-chroot',
|
24
|
+
access_key: '{{ user `aws_access_key` }}',
|
25
|
+
secret_key: '{{ user `aws_secret_key` }}',
|
26
|
+
source_ami: "{{user `source_ami`}}",
|
27
|
+
ami_name: 'packer-image.{{ user `image_name` }} {{timestamp}}',
|
28
|
+
device_path: '/dev/{{ user `ebs_device` }}'
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
PROVISIONER_COMMANDS = [
|
32
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/proc/sys/fs/binfmt_misc',
|
33
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/proc',
|
34
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/sys',
|
35
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/dev/pts',
|
36
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/dev',
|
37
|
+
'mv /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/etc/network /tmp/{{ user `ebs_device`}}-network',
|
38
|
+
'umount /dev/{{ user `ebs_device` }}1',
|
39
|
+
'mkfs.ext4 /dev/{{ user `ebs_device` }}1',
|
40
|
+
'tune2fs -L cloudimg-rootfs /dev/{{ user `ebs_device` }}1',
|
41
|
+
'mount /dev/{{ user `ebs_device` }}1 /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}',
|
42
|
+
'tar -C /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }} -xpf {{ user `source_image_path` }}',
|
43
|
+
'cp /etc/resolv.conf /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/etc',
|
44
|
+
'echo "LABEL=cloudimg-rootfs / ext4 defaults,discard 0 0" > /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/etc/fstab',
|
45
|
+
'mount -t proc none /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/proc',
|
46
|
+
'mount -o bind /sys /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/sys',
|
47
|
+
'mount -o bind /dev /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/dev',
|
48
|
+
# Sadly we need to install grub inside the image, and this implementation is Ubunut specific. This can be patched eventually when needed
|
49
|
+
'chroot /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }} apt-get update',
|
50
|
+
'chroot /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }} apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y --force-yes --no-upgrade install grub-pc grub-legacy-ec2',
|
51
|
+
'chroot /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }} update-grub',
|
52
|
+
'grub-install --root-directory=/mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }} /dev/{{ user `ebs_device` }}',
|
53
|
+
'rm -rf /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/etc/network',
|
54
|
+
'mv /tmp/{{ user `ebs_device`}}-network /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/etc/network',
|
55
|
+
'rm -f /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/etc/init/fake-container-events.conf', # HACK
|
56
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/proc',
|
57
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/sys',
|
58
|
+
'umount /mnt/packer-amazon-chroot-volumes/{{ user `ebs_device` }}/dev'
|
59
|
+
]
|
60
|
+
|
61
|
+
def initialize(config)
|
62
|
+
system("#{packer_path} --version")
|
63
|
+
@hvm = config[:hvm] || true
|
64
|
+
@root_size = config[:root_size] || 10
|
65
|
+
@region = config[:region] || 'us-east-1'
|
66
|
+
@copy_regions = config[:copy_regions] || []
|
67
|
+
@accounts = config[:account_ids] || []
|
68
|
+
@source_ami = config[:source_ami] || find_ami
|
69
|
+
@write_txt = Linecook.config[:packager] && Linecook.config[:packager][:ami] && Linecook.config[:packager][:ami][:update_txt]
|
70
|
+
end
|
71
|
+
|
72
|
+
def package(image)
|
73
|
+
@image = image
|
74
|
+
conf_file = Tempfile.new("#{@image.id}-packer.json")
|
75
|
+
config = generate_config
|
76
|
+
conf_file.write(config)
|
77
|
+
conf_file.close
|
78
|
+
output = []
|
79
|
+
PTY.spawn("sudo #{PACKER_PATH} build -machine-readable #{conf_file.path}") do |stdout, _, _|
|
80
|
+
begin
|
81
|
+
stdout.each do |line|
|
82
|
+
output << line if line =~ /artifact/
|
83
|
+
tokens = line.split(',')
|
84
|
+
if tokens.length > 4
|
85
|
+
out = tokens[4].gsub('%!(PACKER_COMMA)', ',')
|
86
|
+
time = DateTime.strptime(tokens[0], '%s').strftime('%c')
|
87
|
+
puts "#{time} | #{out}"
|
88
|
+
else
|
89
|
+
puts "unexpected output format"
|
90
|
+
puts tokens
|
91
|
+
end
|
92
|
+
end
|
93
|
+
rescue Errno::EIO
|
94
|
+
puts "Packer finshed executing"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
extract_amis_from_output(output)
|
98
|
+
ensure
|
99
|
+
conf_file.close
|
100
|
+
conf_file.unlink
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# TO DO:
|
106
|
+
# support for multiple accounts, multiple regions
|
107
|
+
# code to extract ami name(s) from output
|
108
|
+
# amis = `grep "amazon-ebs,artifact,0,id" packer.log`.chomp.split(',')[5].split('%!(PACKER_COMMA)')
|
109
|
+
# route53 TXT record integration
|
110
|
+
|
111
|
+
def generate_config
|
112
|
+
config = {
|
113
|
+
variables: {
|
114
|
+
aws_access_key: Linecook.config[:aws][:access_key],
|
115
|
+
aws_secret_key: Linecook.config[:aws][:secret_key],
|
116
|
+
ebs_device: free_device,
|
117
|
+
source_ami: @source_ami,
|
118
|
+
image_name: "linecook-#{@image.id}",
|
119
|
+
source_image_path: @image.path
|
120
|
+
},
|
121
|
+
builders: [
|
122
|
+
BUILDER_CONFIG.merge(
|
123
|
+
ami_users: @accounts,
|
124
|
+
ami_regions: @copy_regions,
|
125
|
+
ami_virtualization_type: virt_type,
|
126
|
+
root_volume_size: @root_size
|
127
|
+
)
|
128
|
+
],
|
129
|
+
provisioners: build_provisioner(PROVISIONER_COMMANDS)
|
130
|
+
}
|
131
|
+
JSON.pretty_generate(config)
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_provisioner(commands)
|
135
|
+
provisioner = []
|
136
|
+
commands.each do |cmd|
|
137
|
+
provisioner << {
|
138
|
+
type: 'shell-local',
|
139
|
+
command: cmd
|
140
|
+
}
|
141
|
+
end
|
142
|
+
provisioner
|
143
|
+
end
|
144
|
+
|
145
|
+
def packer_path
|
146
|
+
@path ||= begin
|
147
|
+
found = File.exists?(PACKER_PATH) ? PACKER_PATH : find_executable('packer')
|
148
|
+
path = if found
|
149
|
+
version = `#{found} --version`
|
150
|
+
Gem::Version.new(version) >= Gem::Version.new(PACKER_VERSION) ? found : nil
|
151
|
+
end
|
152
|
+
path ||= get_packer
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_packer
|
157
|
+
puts "packer too old (<#{PACKER_VERSION}) or not present, getting latest packer"
|
158
|
+
arch = 1.size == 8 ? 'amd64' : '386'
|
159
|
+
path = File.join(File.dirname(PACKER_PATH), 'packer.zip')
|
160
|
+
url = File.join(SOURCE_URL, PACKER_VERSION, "packer_#{PACKER_VERSION}_linux_#{arch}.zip")
|
161
|
+
download(url, path)
|
162
|
+
unzip(path)
|
163
|
+
PACKER_PATH
|
164
|
+
end
|
165
|
+
|
166
|
+
def free_device
|
167
|
+
lock('device_scan')
|
168
|
+
free = nil
|
169
|
+
prefix = device_prefix
|
170
|
+
('f'..'zzz').to_a.each do |suffix|
|
171
|
+
device = "#{prefix}#{suffix}"
|
172
|
+
if free_device?(device)
|
173
|
+
lock(device)
|
174
|
+
at_exit do
|
175
|
+
clear_lock(device)
|
176
|
+
end
|
177
|
+
free = device
|
178
|
+
break
|
179
|
+
end
|
180
|
+
end
|
181
|
+
unlock('device_scan')
|
182
|
+
return free
|
183
|
+
end
|
184
|
+
|
185
|
+
def free_device?(device)
|
186
|
+
!File.exists?("/dev/#{device}") && !File.exists?(lock_path(device))
|
187
|
+
end
|
188
|
+
|
189
|
+
def lock(name)
|
190
|
+
lockfile(name).flock(File::LOCK_EX)
|
191
|
+
end
|
192
|
+
|
193
|
+
def unlock(name)
|
194
|
+
lockfile(name).flock(File::LOCK_UN)
|
195
|
+
lockfile(name).close
|
196
|
+
end
|
197
|
+
|
198
|
+
def clear_lock(name)
|
199
|
+
unlock(name)
|
200
|
+
FileUtils.rm_f(lockfile(name))
|
201
|
+
end
|
202
|
+
|
203
|
+
def lockfile(suffix)
|
204
|
+
@locks ||= {}
|
205
|
+
path = lock_path(suffix)
|
206
|
+
@locks[path] = @locks[path] || File.open(path, File::RDWR|File::CREAT, 0644)
|
207
|
+
end
|
208
|
+
|
209
|
+
def lock_path(suffix)
|
210
|
+
"/tmp/free_device_lock_#{suffix.gsub('/','_')}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def device_prefix
|
214
|
+
prefixes = ['xvd', 'sd']
|
215
|
+
`sudo ls -1 /sys/block`.lines.each do |dev| # FIXME
|
216
|
+
prefixes.each do |prefix|
|
217
|
+
return prefix if dev =~ /^#{prefix}/
|
218
|
+
end
|
219
|
+
end
|
220
|
+
return prefixes.first
|
221
|
+
end
|
222
|
+
|
223
|
+
def extract_amis_from_output(output)
|
224
|
+
amis = output.grep(/amazon-chroot,artifact,0,id/).first.chomp.split(',')[5].split('%!(PACKER_COMMA)')
|
225
|
+
amis.each do |info_str|
|
226
|
+
ami_info = info_str.split(':')
|
227
|
+
ami_region = ami_info[0]
|
228
|
+
ami_id = ami_info[1]
|
229
|
+
puts "Built #{ami_id} for #{ami_region}"
|
230
|
+
Linecook::Route53.upsert_record(@image.name, ami_id, ami_region) if @write_txt
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def virt_type
|
235
|
+
@hvm ? 'hvm' : 'paravirtual'
|
236
|
+
end
|
237
|
+
|
238
|
+
def find_ami
|
239
|
+
url = "http://uec-images.ubuntu.com/query/trusty/server/released.current.txt"
|
240
|
+
data = open(url).read.split("\n").map{|l| l.split}.detect do |ary|
|
241
|
+
ary[4] == 'ebs' &&
|
242
|
+
ary[5] == 'amd64' &&
|
243
|
+
ary[6] == @region &&
|
244
|
+
ary.last == virt_type
|
245
|
+
end
|
246
|
+
data[7]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|