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
@@ -1,11 +0,0 @@
|
|
1
|
-
module Linecook
|
2
|
-
module LinuxBuilder
|
3
|
-
extend self
|
4
|
-
|
5
|
-
def backend
|
6
|
-
config = Linecook.config[:builder]
|
7
|
-
images = Linecook.config[:image][:images]
|
8
|
-
Linecook::Lxc::Container.new(name: config[:name], home: config[:home], image: config[:image], bridge: true, tmp: Linecook.config[:tmp])
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
@@ -1,286 +0,0 @@
|
|
1
|
-
require 'linecook-gem/image/manager'
|
2
|
-
require 'linecook-gem/util/ssh'
|
3
|
-
require 'linecook-gem/util/config'
|
4
|
-
require 'linecook-gem/util/executor'
|
5
|
-
require 'tempfile'
|
6
|
-
require 'ipaddress'
|
7
|
-
|
8
|
-
module Linecook
|
9
|
-
module Lxc
|
10
|
-
class Container
|
11
|
-
|
12
|
-
include Executor
|
13
|
-
MAX_WAIT = 60
|
14
|
-
attr_reader :config, :root
|
15
|
-
def initialize(name: 'linecook', home: '/u/lxc', image: nil, remote: :local, bridge: false, tmp: nil)
|
16
|
-
@name = name
|
17
|
-
@home = home
|
18
|
-
@bridge = bridge
|
19
|
-
@remote = remote == :local ? false : remote
|
20
|
-
@root = File.join(@home, @name, 'rootfs')
|
21
|
-
@tmp = tmp
|
22
|
-
@tmpmount = "linecook_#{name}"
|
23
|
-
config = { utsname: name, rootfs: @root, mount: {} }
|
24
|
-
config[:mount][:entry] ||= []
|
25
|
-
config[:mount][:entry] << '/sys/fs/cgroup sys/fs/cgroup none bind 0 0'
|
26
|
-
config[:mount][:entry] << "#{capture("mktemp -d --tmpdir=/#{@tmp} #{@tmpmount}XXXXXXX").strip} #{@tmp} none bind,create=dir 0 0" if @tmp
|
27
|
-
|
28
|
-
@config = Linecook::Lxc::Config.generate(config) # FIXME read link from config
|
29
|
-
@source_image = image || :base_image
|
30
|
-
end
|
31
|
-
|
32
|
-
def start
|
33
|
-
return if running?
|
34
|
-
setup_image
|
35
|
-
setup_dirs
|
36
|
-
mount_all
|
37
|
-
write_config
|
38
|
-
execute("lxc-start #{container_str} -d")
|
39
|
-
wait_running
|
40
|
-
setup_bridge if @bridge
|
41
|
-
wait_ssh
|
42
|
-
unmount unless running?
|
43
|
-
end
|
44
|
-
|
45
|
-
def resume
|
46
|
-
execute("lxc-start #{container_str} -d") unless running?
|
47
|
-
end
|
48
|
-
|
49
|
-
def stop(clean: false, destroy: true)
|
50
|
-
setup_dirs
|
51
|
-
if running?
|
52
|
-
cexec("sudo userdel -r -f #{Linecook::Build::USERNAME}") if @remote
|
53
|
-
execute("lxc-stop #{container_str} -k") if running?
|
54
|
-
end
|
55
|
-
unmount(clean: clean) if destroy
|
56
|
-
end
|
57
|
-
|
58
|
-
def ip
|
59
|
-
attempts = 10
|
60
|
-
attempt = 0
|
61
|
-
until info[:ip] || attempt >= attempts
|
62
|
-
attempt += 1
|
63
|
-
sleep(1)
|
64
|
-
end
|
65
|
-
info[:ip].is_a?(Array) ? info[:ip].find { |ip| bridge_network.include?(IPAddress(ip)) } : info[:ip]
|
66
|
-
end
|
67
|
-
|
68
|
-
def running?
|
69
|
-
info[:state] == 'RUNNING'
|
70
|
-
end
|
71
|
-
|
72
|
-
def pid
|
73
|
-
info[:pid]
|
74
|
-
end
|
75
|
-
|
76
|
-
def info
|
77
|
-
@info = {}
|
78
|
-
capture("lxc-info #{container_str} || true").lines.each do |line|
|
79
|
-
k, v = line.strip.split(/:\s+/)
|
80
|
-
key = k.downcase.to_sym
|
81
|
-
@info[key] = @info[key] ? [@info[key]].flatten << v : v
|
82
|
-
end
|
83
|
-
@info
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def cexec(command)
|
89
|
-
execute("lxc-attach #{container_str} -- #{command}")
|
90
|
-
end
|
91
|
-
|
92
|
-
def wait_running
|
93
|
-
wait_for { running? }
|
94
|
-
end
|
95
|
-
|
96
|
-
def wait_ssh
|
97
|
-
if @remote
|
98
|
-
user = Linecook::Build::USERNAME
|
99
|
-
cexec("useradd -m -G sudo #{user} || true")
|
100
|
-
cexec("mkdir -p /home/#{user}/.ssh")
|
101
|
-
Linecook::Builder.ssh.upload(Linecook::SSH.public_key, "/tmp/#{@name}-pubkey")
|
102
|
-
Linecook::Builder.ssh.run("sudo mv /tmp/#{@name}-pubkey #{@root}/home/#{user}/.ssh/authorized_keys")
|
103
|
-
cexec("chown -R #{user} /home/#{user}/.ssh")
|
104
|
-
end
|
105
|
-
wait_for { capture("lxc-attach -n #{@name} -P #{@home} status ssh || true") =~ /running/ }
|
106
|
-
end
|
107
|
-
|
108
|
-
def wait_for
|
109
|
-
attempts = 0
|
110
|
-
until attempts > MAX_WAIT
|
111
|
-
break if yield
|
112
|
-
attempts += 1
|
113
|
-
sleep(1)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def lxc?
|
118
|
-
namespaces = capture('cat /proc/1/cgroup').lines.map{ |l| l.strip.split(':').last }.uniq
|
119
|
-
namespaces.length != 1 || namespaces.first != '/'
|
120
|
-
end
|
121
|
-
|
122
|
-
def setup_dirs
|
123
|
-
@lower_dir = tmpdir(label: 'lower')
|
124
|
-
@upper_base = tmpdir(label: 'upper')
|
125
|
-
@upper_dir = File.join(@upper_base, '/upper')
|
126
|
-
@work_dir = File.join(@upper_base, '/work')
|
127
|
-
@socket_dirs = []
|
128
|
-
(Linecook.config[:socket_dirs] ||[]).each{ |sock| @socket_dirs << File.join(@root, sock) }
|
129
|
-
end
|
130
|
-
|
131
|
-
def write_config
|
132
|
-
path = if @remote
|
133
|
-
file = "/tmp/lxc-config-#{SecureRandom.hex(4)}"
|
134
|
-
@remote.upload(@config, file)
|
135
|
-
file
|
136
|
-
else
|
137
|
-
file = Tempfile.new('lxc-config')
|
138
|
-
file.write(@config)
|
139
|
-
file.close
|
140
|
-
file.path
|
141
|
-
end
|
142
|
-
execute("mv #{path} #{File.join(@home, @name, 'config')}")
|
143
|
-
end
|
144
|
-
|
145
|
-
def mount_all
|
146
|
-
# Prepare an overlayfs
|
147
|
-
execute("mkdir -p #{@root}")
|
148
|
-
execute("mkdir -p #{@lower_dir}")
|
149
|
-
execute("mkdir -p #{@upper_base}")
|
150
|
-
mount(@image_path, @lower_dir, options: '-o loop')
|
151
|
-
mount('tmpfs', @upper_base, type: '-t tmpfs', options:'-o noatime') # FIXME: - don't always be tmpfs
|
152
|
-
execute("mkdir -p #{@work_dir}")
|
153
|
-
execute("mkdir -p #{@upper_dir}")
|
154
|
-
mount('overlay', @root, type: '-t overlay', options: "-o lowerdir=#{@lower_dir},upperdir=#{@upper_dir},workdir=#{@work_dir}")
|
155
|
-
# Overlayfs doesn't support unix domain sockets
|
156
|
-
@socket_dirs.each do |sock|
|
157
|
-
execute("mkdir -p #{sock}")
|
158
|
-
mount('tmpfs', sock, type: '-t tmpfs', options:'-o noatime')
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def mount(source, dest, type: '', options:'')
|
163
|
-
execute("grep -q #{dest} /etc/mtab || sudo mount #{type} #{options} #{source} #{dest}")
|
164
|
-
end
|
165
|
-
|
166
|
-
def unmount(clean: false)
|
167
|
-
@socket_dirs.each { |sock| execute("umount #{sock}") }
|
168
|
-
source = capture("mount | grep #{@lower_dir} | grep squashfs | awk '{print $1}'") if clean
|
169
|
-
execute("umount #{@root}")
|
170
|
-
execute("umount #{@upper_base}")
|
171
|
-
execute("umount #{@lower_dir}")
|
172
|
-
execute("rmdir #{@lower_dir}")
|
173
|
-
execute("rmdir #{@upper_base}")
|
174
|
-
execute("rm -rf /#{@tmp}/#{@tmpmount}*") if @tmp && !@tmp.empty? && @tmp != '/'
|
175
|
-
|
176
|
-
# Clean up the source image, but only if it's not mounted elsewhre
|
177
|
-
FileUtils.rm_f(source) if clean && capture("mount | grep #{source} || true").strip.empty?
|
178
|
-
end
|
179
|
-
|
180
|
-
def bridge_network
|
181
|
-
broad_ip = Socket.getifaddrs.find do |a|
|
182
|
-
a.name == 'lxcbr0' && a.broadaddr && a.broadaddr.afamily == 2 && !a.broadaddr.inspect_sockaddr.start_with?('169.254')
|
183
|
-
end.broadaddr.inspect_sockaddr
|
184
|
-
IPAddress("#{broad_ip.to_s}/24")
|
185
|
-
end
|
186
|
-
|
187
|
-
def setup_bridge
|
188
|
-
bridge_config = <<-eos
|
189
|
-
auto lo
|
190
|
-
iface lo inet loopback
|
191
|
-
|
192
|
-
auto lxcbr0
|
193
|
-
iface lxcbr0 inet dhcp
|
194
|
-
bridge_ports eth0
|
195
|
-
bridge_fd 0
|
196
|
-
bridge_maxwait 0
|
197
|
-
eos
|
198
|
-
interfaces = Tempfile.new('interfaces')
|
199
|
-
interfaces.write(bridge_config)
|
200
|
-
interfaces.close
|
201
|
-
execute("mv #{interfaces.path} #{File.join(@root, 'etc', 'network', 'interfaces')}")
|
202
|
-
|
203
|
-
execute("lxc-attach -n #{@name} -P #{@home} ifup lxcbr0")
|
204
|
-
end
|
205
|
-
|
206
|
-
def setup_image
|
207
|
-
@source_path = Linecook::ImageManager.fetch(@source_image, profile: :public)
|
208
|
-
if @remote
|
209
|
-
name = File.basename(@source_path)
|
210
|
-
dest = "/u/linecook/images/#{name}"
|
211
|
-
unless test("[ -f #{dest} ]") && capture("shasum #{dest}").split.first == `shasum #{@source_path}`.split.first
|
212
|
-
tmp = "/tmp/#{name}-#{SecureRandom.hex(4)}"
|
213
|
-
@remote.run("sudo mkdir -p #{File.dirname(dest)}")
|
214
|
-
@remote.upload(@source_path, tmp)
|
215
|
-
@remote.run("sudo mv #{tmp} #{dest}")
|
216
|
-
end
|
217
|
-
@image_path = dest
|
218
|
-
else
|
219
|
-
@image_path = @source_path
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def tmpdir(label: 'tmp')
|
224
|
-
"/tmp/#{@name}-#{label}"
|
225
|
-
end
|
226
|
-
|
227
|
-
def container_str
|
228
|
-
"-n #{@name} -P #{@home}"
|
229
|
-
end
|
230
|
-
|
231
|
-
end
|
232
|
-
|
233
|
-
module Config
|
234
|
-
extend self
|
235
|
-
DEFAULT_LXC_CONFIG = {
|
236
|
-
include: '/usr/share/lxc/config/ubuntu.common.conf',
|
237
|
-
aa_profile: 'unconfined', #'lxc-container-default-with-nesting',
|
238
|
-
arch: 'x86.64',
|
239
|
-
utsname: 'linecook',
|
240
|
-
rootfs: '/u/lxc/linecook/rootfs',
|
241
|
-
network: {
|
242
|
-
type: 'veth',
|
243
|
-
flags: 'up',
|
244
|
-
link: 'lxcbr0'
|
245
|
-
},
|
246
|
-
mount: {
|
247
|
-
auto: ['cgroup', 'proc:rw', 'sys:rw']
|
248
|
-
},
|
249
|
-
cap: {
|
250
|
-
drop: ''
|
251
|
-
},
|
252
|
-
cgroup: {
|
253
|
-
devices: {
|
254
|
-
allow: 'a'
|
255
|
-
}
|
256
|
-
}
|
257
|
-
}.freeze
|
258
|
-
|
259
|
-
def generate(**kwargs)
|
260
|
-
cfg = []
|
261
|
-
flatten(DEFAULT_LXC_CONFIG.deep_merge(kwargs || {})).each do |k, v|
|
262
|
-
[v].flatten.each do |val|
|
263
|
-
cfg << "lxc.#{k}=#{val}"
|
264
|
-
end
|
265
|
-
end
|
266
|
-
cfg.join("\n")
|
267
|
-
end
|
268
|
-
|
269
|
-
private
|
270
|
-
|
271
|
-
def flatten(hash)
|
272
|
-
flattened = {}
|
273
|
-
hash.each do |k, v|
|
274
|
-
if v.is_a?(Hash)
|
275
|
-
flatten(v).each do |key, val|
|
276
|
-
flattened["#{k}.#{key}"] = val
|
277
|
-
end
|
278
|
-
else
|
279
|
-
flattened[k] = v
|
280
|
-
end
|
281
|
-
end
|
282
|
-
flattened
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'sshkey'
|
3
|
-
|
4
|
-
require 'linecook-gem/builder/lxc'
|
5
|
-
require 'linecook-gem/builder/darwin_backend'
|
6
|
-
require 'linecook-gem/builder/linux_backend'
|
7
|
-
require 'linecook-gem/builder/build'
|
8
|
-
|
9
|
-
module Linecook
|
10
|
-
module Builder
|
11
|
-
extend self
|
12
|
-
extend Forwardable
|
13
|
-
BUILD_HOME = '/u/lxc'
|
14
|
-
|
15
|
-
def_instance_delegators :backend, :stop, :ip, :info, :running?
|
16
|
-
|
17
|
-
def backend
|
18
|
-
@backend ||= backend_for_platform
|
19
|
-
end
|
20
|
-
|
21
|
-
def start
|
22
|
-
return if running?
|
23
|
-
backend.start
|
24
|
-
increase_loop_devices
|
25
|
-
end
|
26
|
-
|
27
|
-
def ssh
|
28
|
-
config = Linecook.config[:builder]
|
29
|
-
@ssh ||= SSH.new(ip, username: config[:username], password: config[:password], keyfile: Linecook::SSH.private_key)
|
30
|
-
end
|
31
|
-
|
32
|
-
def builds
|
33
|
-
ssh.test("[ -d #{BUILD_HOME} ]") ? ssh.capture("find #{BUILD_HOME} -maxdepth 1 -mindepth 1 -type d -printf \"%f\n\"").delete(';').lines : []
|
34
|
-
end
|
35
|
-
|
36
|
-
def build_info
|
37
|
-
info = {}
|
38
|
-
builds.each do |build|
|
39
|
-
info[build] = Linecook::Build.new(build, nil).info
|
40
|
-
end
|
41
|
-
info
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def backend_for_platform
|
47
|
-
case Config.platform
|
48
|
-
when 'linux'
|
49
|
-
LinuxBuilder.backend
|
50
|
-
when 'darwin'
|
51
|
-
OSXBuilder.backend
|
52
|
-
else
|
53
|
-
fail "Cannot find supported backend for #{Config.platform}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def increase_loop_devices
|
58
|
-
kparams = {}
|
59
|
-
|
60
|
-
ssh.capture('cat /proc/cmdline').split(/\s+/).each do |param|
|
61
|
-
k,v = param.split('=')
|
62
|
-
kparams[k] = v
|
63
|
-
end
|
64
|
-
|
65
|
-
if loops = kparams['max_loop']
|
66
|
-
current = ssh.capture('ls -1 /dev | grep loop')
|
67
|
-
last_loop = current.lines.length
|
68
|
-
to_create = loops.to_i - last_loop
|
69
|
-
|
70
|
-
to_create.times do |count|
|
71
|
-
index = last_loop + count
|
72
|
-
ssh.run("[ ! -e /dev/loop#{index} ] && sudo mknod -m660 /dev/loop#{index} b 7 #{index}")
|
73
|
-
ssh.run("sudo chown root:disk /dev/loop#{index}")
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'octokit'
|
2
|
-
|
3
|
-
require 'linecook-gem/util/config'
|
4
|
-
|
5
|
-
module Linecook
|
6
|
-
module GithubManager
|
7
|
-
extend self
|
8
|
-
|
9
|
-
def url(name)
|
10
|
-
latest[:assets].find { |a| a[:name] =~ /#{name}/ }[:browser_download_url]
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def client
|
16
|
-
@client ||= Octokit::Client.new
|
17
|
-
end
|
18
|
-
|
19
|
-
def source
|
20
|
-
@source ||= (Linecook.config['source_repo'] || 'dalehamel/lxb')
|
21
|
-
end
|
22
|
-
|
23
|
-
def latest
|
24
|
-
client.releases(source).sort_by { |r| r[:published_at] }.last
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
|
3
|
-
require 'linecook-gem/image/s3'
|
4
|
-
require 'linecook-gem/image/github'
|
5
|
-
require 'linecook-gem/image/crypt'
|
6
|
-
|
7
|
-
module Linecook
|
8
|
-
module ImageManager
|
9
|
-
IMAGE_PATH = File.join(Config::LINECOOK_HOME, 'images').freeze
|
10
|
-
extend self
|
11
|
-
|
12
|
-
def fetch(image, upgrade:false, profile: :private, type: nil, encrypted: false)
|
13
|
-
url_path = if image.is_a?(Symbol)
|
14
|
-
image_name = Linecook.config[:image][:images][image][:name]
|
15
|
-
path = File.join(IMAGE_PATH, image_name)
|
16
|
-
provider(profile).url(image_name) unless File.exist?(path) || upgrade
|
17
|
-
elsif image.is_a?(Hash)
|
18
|
-
profile = :private
|
19
|
-
encrypted = true
|
20
|
-
name = image[:name] == :latest ? File.basename(latest(image[:type])) : image[:name]
|
21
|
-
path = File.join([IMAGE_PATH, image[:type], name].compact)
|
22
|
-
provider(profile).url(name, type: image[:type])
|
23
|
-
else
|
24
|
-
puts "#{image} is invalid"
|
25
|
-
end
|
26
|
-
|
27
|
-
Linecook::Downloader.download(url_path, path, encrypted: encrypted) unless File.exist?(path) || upgrade
|
28
|
-
path
|
29
|
-
end
|
30
|
-
|
31
|
-
def clean(type: nil)
|
32
|
-
Dir["#{File.join([IMAGE_PATH, type].compact)}/**/*"].each do |image|
|
33
|
-
FileUtils.rm_f(image) unless `mount`.index(image)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def upload(image, profile: :private, type: nil)
|
38
|
-
path = File.join(IMAGE_PATH, File.basename(image))
|
39
|
-
puts "Encrypting and uploading image #{path}"
|
40
|
-
encrypted = Linecook::Crypto.new.encrypt_file(path)
|
41
|
-
provider(profile).upload(encrypted, type: type)
|
42
|
-
FileUtils.rm_f(encrypted)
|
43
|
-
end
|
44
|
-
|
45
|
-
def url(image, profile: :private, type: nil)
|
46
|
-
provider(profile).url(image, type: type)
|
47
|
-
end
|
48
|
-
|
49
|
-
def list(type: nil, profile: :private)
|
50
|
-
profile = profile.to_sym
|
51
|
-
provider(profile).list(type: type)
|
52
|
-
end
|
53
|
-
|
54
|
-
def latest(type, profile: :private)
|
55
|
-
profile = profile.to_sym
|
56
|
-
provider(profile).latest(type)
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def provider(image_profile)
|
62
|
-
profile = Linecook.config[:image][:provider][image_profile]
|
63
|
-
case profile
|
64
|
-
when :s3
|
65
|
-
S3Manager
|
66
|
-
when :github
|
67
|
-
GithubManager
|
68
|
-
else
|
69
|
-
fail "No provider implemented for for #{profile}"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|