linecook-gem 0.0.3 → 0.0.4
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/builder/build.rb +28 -0
- data/lib/linecook/{darwin_backend.rb → builder/darwin_backend.rb} +4 -1
- data/lib/linecook/{linux_backend.rb → builder/linux_backend.rb} +3 -3
- data/lib/linecook/{lxc.rb → builder/lxc.rb} +25 -43
- data/lib/linecook/builder/manager.rb +56 -0
- data/lib/linecook/cli.rb +77 -12
- data/lib/linecook/image/crypt.rb +49 -0
- data/lib/linecook/image/github.rb +27 -0
- data/lib/linecook/image/manager.rb +43 -0
- data/lib/linecook/image/s3.rb +47 -0
- data/lib/linecook/packager/ebs.rb +270 -0
- data/lib/linecook/packager/manager.rb +23 -0
- data/lib/linecook/{chef.rb → provisioner/chef-zero.rb} +3 -2
- data/lib/linecook/provisioner/manager.rb +34 -0
- data/lib/linecook/provisioner/packer.rb +82 -0
- data/lib/linecook/{config.rb → util/config.rb} +44 -4
- data/lib/linecook/util/downloader.rb +35 -0
- data/lib/linecook/util/executor.rb +33 -0
- data/lib/linecook/{ssh.rb → util/ssh.rb} +59 -2
- data/lib/linecook/version.rb +1 -1
- data/lib/linecook.rb +5 -4
- metadata +76 -11
- data/lib/linecook/bake.rb +0 -21
- data/lib/linecook/build.rb +0 -21
- data/lib/linecook/builder.rb +0 -93
- data/lib/linecook/image.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6a9328562fa6cf23fc9c9d97500f929b07299e3
|
4
|
+
data.tar.gz: 70c988ff74e983fed3ae8e74c20c3cd59ea6baaa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7d3c9ce2562d88673b7bed970b8411c1a560d394e8c5401f982b5608464ca9a2c38026fbf825bf55b6097c5145c305bc3489cd21ac9e2a934d56770d513a9ea
|
7
|
+
data.tar.gz: 5856d130e60480ca800ed090ef5e59b016e627225ca4f4ba6b00a9d9e6b6498218f0fd3737ea026802cb1b38efb2b73f60810199a521632d8189929043309542
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'linecook/builder/manager'
|
3
|
+
|
4
|
+
module Linecook
|
5
|
+
class Build
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_instance_delegators :@container, :stop, :start, :ip, :info
|
9
|
+
|
10
|
+
def initialize(name, image: nil)
|
11
|
+
Linecook::Builder.start
|
12
|
+
@name = name
|
13
|
+
@image = image || Linecook::Config.load_config[:provisioner][:default_image]
|
14
|
+
@container = Linecook::Lxc::Container.new(name: @name, image: @image, remote: Linecook::Builder.ssh)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ssh
|
18
|
+
@ssh ||= Linecook::SSH.new(@container.ip, username: 'ubuntu', password: 'ubuntu', proxy: Linecook::Builder.ssh, keyfile: Linecook::SSH.private_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def snapshot(save: false)
|
22
|
+
path = "/tmp/#{@name}-#{Time.now.to_i}.squashfs"
|
23
|
+
Linecook::Builder.ssh.run("sudo mksquashfs #{@container.root} #{path} -wildcards -e 'usr/src' 'var/lib/apt/lists/archive*' 'var/cache/apt/archives'") # FIXME make these excludes dynamic based on OS
|
24
|
+
Linecook::Builder.ssh.download(path, local: File.join(Linecook::ImageManager::IMAGE_PATH, File.basename(path))) if save
|
25
|
+
path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Linecook
|
2
2
|
module LinuxBuilder
|
3
3
|
extend self
|
4
|
-
LXC_MIN_VERSION = '1.
|
4
|
+
LXC_MIN_VERSION = '1.1.4'
|
5
5
|
|
6
6
|
def backend
|
7
7
|
check_lxc_version
|
8
8
|
config = Linecook::Config.load_config[:builder]
|
9
|
-
images = Linecook::Config.load_config[:images]
|
10
|
-
Linecook::Lxc::Container.new(name: config[:name], home: config[:home], image:
|
9
|
+
images = Linecook::Config.load_config[:image][:images]
|
10
|
+
Linecook::Lxc::Container.new(name: config[:name], home: config[:home], image: config[:image], bridge: true)
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -1,34 +1,37 @@
|
|
1
|
-
require 'linecook/
|
2
|
-
require 'linecook/
|
3
|
-
require 'linecook/config'
|
1
|
+
require 'linecook/image/manager'
|
2
|
+
require 'linecook/util/ssh'
|
3
|
+
require 'linecook/util/config'
|
4
|
+
require 'linecook/util/executor'
|
4
5
|
require 'tempfile'
|
5
6
|
require 'ipaddress'
|
6
7
|
|
7
8
|
module Linecook
|
8
9
|
module Lxc
|
9
10
|
class Container
|
11
|
+
|
12
|
+
include Executor
|
10
13
|
MAX_WAIT = 60
|
11
|
-
attr_reader :config
|
12
|
-
def initialize(name: 'linecook', home: '/u/lxc', image: nil, remote: :local)
|
13
|
-
@remote = remote == :local ? false : remote
|
14
|
-
config = { utsname: name, rootfs: File.join(home, name, 'rootfs') }
|
15
|
-
config.merge!(network: { type: 'veth', flags: 'up', link: 'lxcbr0' }) # FIXME
|
16
|
-
@config = Linecook::Lxc::Config.generate(config) # FIXME read link from config
|
17
|
-
@source_image = image || Linecook::Config.load_config[:images][:base_image]
|
14
|
+
attr_reader :config, :root
|
15
|
+
def initialize(name: 'linecook', home: '/u/lxc', image: nil, remote: :local, bridge: false)
|
18
16
|
@name = name
|
19
17
|
@home = home
|
18
|
+
@bridge = bridge
|
19
|
+
@remote = remote == :local ? false : remote
|
20
|
+
@root = File.join(@home, @name, 'rootfs')
|
21
|
+
config = { utsname: name, rootfs: @root }
|
22
|
+
@config = Linecook::Lxc::Config.generate(config) # FIXME read link from config
|
23
|
+
@source_image = image || :base_image
|
20
24
|
end
|
21
25
|
|
22
26
|
def start
|
27
|
+
return if running?
|
23
28
|
setup_image
|
24
29
|
setup_dirs
|
25
30
|
mount_all
|
26
31
|
write_config
|
27
32
|
execute("lxc-start #{container_str} -d")
|
28
33
|
wait_running
|
29
|
-
|
30
|
-
execute('[ -f /etc/init/cgmanager.conf ] && sudo status cgmanager | grep -q running && sudo stop cgmanager || true') if lxc?
|
31
|
-
setup_bridge unless @remote
|
34
|
+
setup_bridge if @bridge
|
32
35
|
wait_ssh
|
33
36
|
unmount unless running?
|
34
37
|
end
|
@@ -59,7 +62,7 @@ module Linecook
|
|
59
62
|
|
60
63
|
def info
|
61
64
|
@info = {}
|
62
|
-
capture("lxc-info #{container_str}").lines.each do |line|
|
65
|
+
capture("lxc-info #{container_str} || true").lines.each do |line|
|
63
66
|
k, v = line.strip.split(/:\s+/)
|
64
67
|
key = k.downcase.to_sym
|
65
68
|
@info[key] = @info[key] ? [@info[key]].flatten << v : v
|
@@ -95,9 +98,8 @@ module Linecook
|
|
95
98
|
@upper_base = tmpdir(label: 'upper')
|
96
99
|
@upper_dir = File.join(@upper_base, '/upper')
|
97
100
|
@work_dir = File.join(@upper_base, '/work')
|
98
|
-
@overlay = File.join(@home, @name, '/rootfs')
|
99
101
|
@socket_dirs = []
|
100
|
-
(Linecook::Config.load_config[:socket_dirs] ||[]).each{ |sock| @socket_dirs << File.join(@
|
102
|
+
(Linecook::Config.load_config[:socket_dirs] ||[]).each{ |sock| @socket_dirs << File.join(@root, sock) }
|
101
103
|
end
|
102
104
|
|
103
105
|
def write_config
|
@@ -115,14 +117,14 @@ module Linecook
|
|
115
117
|
|
116
118
|
def mount_all
|
117
119
|
# Prepare an overlayfs
|
118
|
-
execute("mkdir -p #{@
|
120
|
+
execute("mkdir -p #{@root}")
|
119
121
|
execute("mkdir -p #{@lower_dir}")
|
120
122
|
execute("mkdir -p #{@upper_base}")
|
121
123
|
mount(@image_path, @lower_dir, options: '-o loop')
|
122
124
|
mount('tmpfs', @upper_base, type: '-t tmpfs', options:'-o noatime') # FIXME: - don't always be tmpfs
|
123
125
|
execute("mkdir -p #{@work_dir}")
|
124
126
|
execute("mkdir -p #{@upper_dir}")
|
125
|
-
mount('overlay', @
|
127
|
+
mount('overlay', @root, type: '-t overlay', options: "-o lowerdir=#{@lower_dir},upperdir=#{@upper_dir},workdir=#{@work_dir}")
|
126
128
|
# Overlayfs doesn't support unix domain sockets
|
127
129
|
@socket_dirs.each do |sock|
|
128
130
|
execute("mkdir -p #{sock}")
|
@@ -136,7 +138,7 @@ module Linecook
|
|
136
138
|
|
137
139
|
def unmount
|
138
140
|
@socket_dirs.each { |sock| execute("umount #{sock}") }
|
139
|
-
execute("umount #{@
|
141
|
+
execute("umount #{@root}")
|
140
142
|
execute("umount #{@upper_base}")
|
141
143
|
execute("umount #{@lower_dir}")
|
142
144
|
execute("rmdir #{@lower_dir}")
|
@@ -164,17 +166,17 @@ eos
|
|
164
166
|
interfaces = Tempfile.new('interfaces')
|
165
167
|
interfaces.write(bridge_config)
|
166
168
|
interfaces.close
|
167
|
-
execute("mv #{interfaces.path} #{File.join(@
|
169
|
+
execute("mv #{interfaces.path} #{File.join(@root, 'etc', 'network', 'interfaces')}")
|
168
170
|
|
169
171
|
execute("lxc-attach -n #{@name} -P #{@home} ifup lxcbr0")
|
170
172
|
end
|
171
173
|
|
172
174
|
|
173
175
|
def setup_image
|
174
|
-
@source_path = Linecook::
|
176
|
+
@source_path = Linecook::ImageManager.fetch(@source_image)
|
175
177
|
if @remote
|
176
178
|
dest = "#{File.basename(@source_path)}"
|
177
|
-
@remote.upload(@source_path, dest) unless
|
179
|
+
@remote.upload(@source_path, dest) unless test("[ -f #{dest} ]")
|
178
180
|
@image_path = dest
|
179
181
|
else
|
180
182
|
@image_path = @source_path
|
@@ -189,26 +191,6 @@ eos
|
|
189
191
|
"-n #{@name} -P #{@home}"
|
190
192
|
end
|
191
193
|
|
192
|
-
def capture(command, sudo: true)
|
193
|
-
execute(command, sudo: sudo, capture: true)
|
194
|
-
end
|
195
|
-
|
196
|
-
def execute(command, sudo: true, capture: false)
|
197
|
-
command = "sudo #{command}" if sudo
|
198
|
-
if @remote
|
199
|
-
if capture
|
200
|
-
return @remote.capture(command)
|
201
|
-
else
|
202
|
-
@remote.run(command)
|
203
|
-
end
|
204
|
-
else
|
205
|
-
if capture
|
206
|
-
return `#{command}`
|
207
|
-
else
|
208
|
-
system(command)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
194
|
end
|
213
195
|
|
214
196
|
module Config
|
@@ -222,7 +204,7 @@ eos
|
|
222
204
|
network: {
|
223
205
|
type: 'veth',
|
224
206
|
flags: 'up',
|
225
|
-
link: '
|
207
|
+
link: 'lxcbr0'
|
226
208
|
},
|
227
209
|
mount: {
|
228
210
|
auto: 'cgroup'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'sshkey'
|
3
|
+
|
4
|
+
require 'linecook/builder/lxc'
|
5
|
+
require 'linecook/builder/darwin_backend'
|
6
|
+
require 'linecook/builder/linux_backend'
|
7
|
+
require 'linecook/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
|
+
end
|
25
|
+
|
26
|
+
def ssh
|
27
|
+
config = Linecook::Config.load_config[:builder]
|
28
|
+
@ssh ||= SSH.new(ip, username: config[:username], password: config[:password], keyfile: Linecook::SSH.private_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
def builds
|
32
|
+
ssh.test("[ -d #{BUILD_HOME} ]") ? ssh.capture("find #{BUILD_HOME} -maxdepth 1 -mindepth 1 -type d -printf \"%f\n\"").delete(';').lines : []
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_info
|
36
|
+
info = {}
|
37
|
+
builds.each do |build|
|
38
|
+
info[build] = Linecook::Build.new(build, nil).info
|
39
|
+
end
|
40
|
+
info
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def backend_for_platform
|
46
|
+
case Config.platform
|
47
|
+
when 'linux'
|
48
|
+
LinuxBuilder.backend
|
49
|
+
when 'darwin'
|
50
|
+
OSXBuilder.backend
|
51
|
+
else
|
52
|
+
fail "Cannot find supported backend for #{Config.platform}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/linecook/cli.rb
CHANGED
@@ -1,6 +1,56 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'linecook'
|
3
3
|
|
4
|
+
class Crypto < Thor
|
5
|
+
desc 'keygen', 'Generate AES key for securing images'
|
6
|
+
def keygen
|
7
|
+
puts Linecook::Crypto.keygen
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'decrypt IMAGE', ''
|
11
|
+
def decrypt(image)
|
12
|
+
puts Linecook::Crypto.new.decrypt_file(image)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'encrypt IMAGE', ''
|
16
|
+
def encrypt(image)
|
17
|
+
puts Linecook::Crypto.new.encrypt_file(image)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Image < Thor
|
22
|
+
desc 'crypto SUBCOMMAND', 'Manage image encryption'
|
23
|
+
subcommand 'crypto', Crypto
|
24
|
+
|
25
|
+
desc 'list', 'List images' # add remote flag
|
26
|
+
def list
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'fetch IMAGE_NAME', 'Fetch an image by name'
|
30
|
+
method_options name: :string
|
31
|
+
def fetch(name)
|
32
|
+
Linecook::ImageManager.fetch(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'upload IMAGE', 'Upload an image'
|
36
|
+
method_options name: :string
|
37
|
+
def upload(image)
|
38
|
+
Linecook::ImageManager.upload(image)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'url IMAGE', 'Get URL for image'
|
42
|
+
method_options image: :string
|
43
|
+
def url(image)
|
44
|
+
puts Linecook::ImageManager.url(image)
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'package IMAGE', 'Package image'
|
48
|
+
method_options image: :string
|
49
|
+
def package(image)
|
50
|
+
Linecook::Packager.package(image)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
4
54
|
class Builder < Thor
|
5
55
|
desc 'info', 'Show builder info'
|
6
56
|
def info
|
@@ -29,27 +79,42 @@ class Build < Thor
|
|
29
79
|
puts Linecook::Builder.builds
|
30
80
|
end
|
31
81
|
|
32
|
-
desc 'info', 'Show build info'
|
82
|
+
desc 'info', 'Show build info'
|
33
83
|
def info
|
34
|
-
|
84
|
+
end
|
85
|
+
|
86
|
+
desc 'ip', 'Show IP address for build'
|
87
|
+
def ip
|
88
|
+
end
|
89
|
+
|
90
|
+
desc 'snapshot', 'Take a snapshot of the build with NAME'
|
91
|
+
method_option :name, type: :string, required: true, banner: 'ROLE_NAME', desc: 'Name of the role to build', aliases: '-n'
|
92
|
+
def snapshot
|
93
|
+
build = Linecook::Build.new(name, '')
|
94
|
+
build.snapshot(save: true)
|
35
95
|
end
|
36
96
|
end
|
37
97
|
|
38
98
|
class Linecook::CLI < Thor
|
39
|
-
|
99
|
+
|
100
|
+
desc 'image SUBCOMMAND', 'Manage linecook images.'
|
101
|
+
subcommand 'image', Image
|
102
|
+
|
103
|
+
desc 'builder SUBCOMMAND', 'Manage the builder.'
|
40
104
|
subcommand 'builder', Builder
|
41
105
|
|
42
|
-
desc 'build SUBCOMMAND', 'Manage builds'
|
106
|
+
desc 'build SUBCOMMAND', 'Manage running and completed builds.'
|
43
107
|
subcommand 'build', Build
|
44
108
|
|
45
|
-
desc 'bake', 'Bake a new image'
|
109
|
+
desc 'bake', 'Bake a new image.'
|
110
|
+
method_option :name, type: :string, required: true, banner: 'ROLE_NAME', desc: 'Name of the role to build', aliases: '-n'
|
111
|
+
method_option :image, type: :string, banner: 'SOURCE_IMAGE', desc: 'Source image to seed the build.', aliases: '-i'
|
112
|
+
method_option :build, type: :boolean, default: true, desc: 'Build the image', aliases: '-b'
|
113
|
+
method_option :snapshot, type: :boolean, default: false, desc: 'Snapshot the resulting build to create an image', aliases: '-s'
|
114
|
+
method_option :upload, type: :boolean, default: false, desc: 'Upload the resulting build. Implies --snapshot and --encrypt.', aliases: '-u'
|
115
|
+
method_option :package, type: :boolean, default: false, desc: 'Package the resulting image. Implies --upload, --snapshot and --encrypt.', aliases: '-p'
|
46
116
|
def bake
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
desc 'fetch IMAGE_NAME', 'Fetch an image by name'
|
51
|
-
method_options name: :string
|
52
|
-
def fetch(name)
|
53
|
-
Linecook::ImageFetcher.fetch(name)
|
117
|
+
opts = options.symbolize_keys
|
118
|
+
Linecook::Baker.bake(**opts)
|
54
119
|
end
|
55
120
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'encryptor'
|
3
|
+
|
4
|
+
require 'linecook/image/manager'
|
5
|
+
require 'linecook/util/executor'
|
6
|
+
require 'linecook/util/config'
|
7
|
+
|
8
|
+
module Linecook
|
9
|
+
class Crypto
|
10
|
+
include Executor
|
11
|
+
CIPHER = 'aes-256-cbc'
|
12
|
+
KEY_BYTES = 32 # 256 bits
|
13
|
+
attr_reader :iv, :secret_key
|
14
|
+
|
15
|
+
def initialize(remote: nil)
|
16
|
+
@remote = remote
|
17
|
+
load_key
|
18
|
+
end
|
19
|
+
|
20
|
+
def encrypt_image(image)
|
21
|
+
image_path = File.join(Linecook::ImageManager::IMAGE_PATH,File.basename(image))
|
22
|
+
encrypt_file(image_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def encrypt_file(source, dest: nil, keypath: nil)
|
26
|
+
dest ||= "/tmp/#{File.basename(source)}"
|
27
|
+
capture("openssl enc -#{CIPHER} -out #{dest} -in #{source} -K #{@secret_key} -iv #{@iv}")
|
28
|
+
dest
|
29
|
+
end
|
30
|
+
|
31
|
+
def decrypt_file(source, dest: nil, keypath: nil)
|
32
|
+
dest ||= "/tmp/#{File.basename(source)}-decrypted"
|
33
|
+
capture("openssl enc -#{CIPHER} -out #{dest} -in #{source} -K #{@secret_key} -iv #{@iv} -d")
|
34
|
+
dest
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.keygen
|
38
|
+
iv = OpenSSL::Cipher::Cipher.new(CIPHER).random_iv.unpack('H*').first
|
39
|
+
secret_key = Base64.encode64(OpenSSL::Random.random_bytes(KEY_BYTES)).unpack('H*').first
|
40
|
+
"[:IV:#{iv}:KY:#{secret_key}]"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def load_key
|
46
|
+
@iv, @secret_key = Linecook::Config.secrets['aeskey'].match(/\[:IV:(.+):KY:(.+)\]/m).captures
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
|
3
|
+
require 'linecook/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 ||= (Config.load_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
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'linecook/image/crypt'
|
2
|
+
require 'linecook/image/github'
|
3
|
+
require 'linecook/image/s3'
|
4
|
+
require 'linecook/util/downloader'
|
5
|
+
|
6
|
+
module Linecook
|
7
|
+
module ImageManager
|
8
|
+
IMAGE_PATH = File.join(Config::LINECOOK_HOME, 'images').freeze
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def fetch(name, upgrade:false, profile: :private)
|
12
|
+
image_name = Linecook::Config.load_config[:image][:images][name][:name]
|
13
|
+
path = File.join(IMAGE_PATH, image_name)
|
14
|
+
url = provider(profile).url(name) unless File.exist?(path) || upgrade# FIXME
|
15
|
+
Linecook::Downloader.download(url, path) unless File.exist?(path) || upgrade
|
16
|
+
path
|
17
|
+
end
|
18
|
+
|
19
|
+
def upload(image, profile: :private)
|
20
|
+
path = File.join(IMAGE_PATH, File.basename(image))
|
21
|
+
puts "Encrypting and uploading image #{path}"
|
22
|
+
provider(profile).upload(Linecook::Crypto.new.encrypt_file(path))
|
23
|
+
end
|
24
|
+
|
25
|
+
def url(image, profile: :private)
|
26
|
+
provider(profile).url("builds/#{image}")
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def provider(image_profile)
|
32
|
+
profile = Linecook::Config.load_config[:image][:provider][image_profile]
|
33
|
+
case profile
|
34
|
+
when :s3
|
35
|
+
S3Manager
|
36
|
+
when :github
|
37
|
+
GithubManager
|
38
|
+
else
|
39
|
+
fail "No provider implemented for for #{profile}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'ruby-progressbar'
|
2
|
+
require 'aws-sdk'
|
3
|
+
|
4
|
+
module Linecook
|
5
|
+
module S3Manager
|
6
|
+
extend self
|
7
|
+
EXPIRY = 20
|
8
|
+
|
9
|
+
def url(name)
|
10
|
+
client
|
11
|
+
s3 = Aws::S3::Resource.new
|
12
|
+
obj = s3.bucket(Linecook::Config.secrets['bucket']).object(name)
|
13
|
+
obj.presigned_url(:get, expires_in: EXPIRY * 60)
|
14
|
+
end
|
15
|
+
|
16
|
+
def upload(path)
|
17
|
+
File.open(path, 'rb') do |file|
|
18
|
+
fname = File.basename(path)
|
19
|
+
pbar = ProgressBar.create(title: fname, total: file.size)
|
20
|
+
common_opts = { bucket: Linecook::Config.secrets['bucket'], key: File.join('builds', fname) }
|
21
|
+
resp = client.create_multipart_upload(storage_class: 'REDUCED_REDUNDANCY', server_side_encryption: 'AES256', **common_opts)
|
22
|
+
id = resp.upload_id
|
23
|
+
part = 0
|
24
|
+
total = 0
|
25
|
+
parts = []
|
26
|
+
while content = file.read(1048576 * 20)
|
27
|
+
part += 1
|
28
|
+
resp = client.upload_part(body: content, content_length: content.length, part_number: part, upload_id: id, **common_opts)
|
29
|
+
parts << { etag: resp.etag, part_number: part }
|
30
|
+
total += content.length
|
31
|
+
pbar.progress = total
|
32
|
+
pbar.title = "#{fname} - (#{((total.to_f/file.size.to_f)*100.0).round(2)}%)"
|
33
|
+
end
|
34
|
+
client.complete_multipart_upload(upload_id: id, multipart_upload: { parts: parts }, **common_opts)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def client
|
40
|
+
@client ||= begin
|
41
|
+
Aws.config[:credentials] = Aws::Credentials.new(Linecook::Config.secrets['aws_access_key'], Linecook::Config.secrets['aws_secret_key'])
|
42
|
+
Aws.config[:region] = 'us-east-1'
|
43
|
+
Aws::S3::Client.new
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|