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.
@@ -1,47 +0,0 @@
1
- require 'securerandom'
2
- require 'fileutils'
3
-
4
- require 'linecook-gem/builder/build'
5
- require 'linecook-gem/provisioner/chef-zero'
6
- require 'linecook-gem/provisioner/packer'
7
-
8
- module Linecook
9
- module Baker
10
- extend self
11
-
12
- def bake(name: nil, tag: nil, id: nil, snapshot: nil, upload: nil, package: nil, build: nil, keep: nil, clean: nil)
13
- build_agent = Linecook::Build.new(name, tag: tag, id: id, image: image(name))
14
- resume = clean ? false : true
15
- provider(name).provision(build_agent, name) if build
16
- snapshot = build_agent.snapshot(save: true, resume: resume) if snapshot || upload || package
17
- Linecook::ImageManager.upload(snapshot, type: build_agent.type) if upload || package
18
- Linecook::Packager.package(snapshot, type: build_agent.type, ami: true) if package
19
- rescue => e
20
- puts e.message
21
- puts e.backtrace
22
- raise e
23
- ensure
24
- build_agent.stop(clean: clean) unless keep
25
- build_agent.clean if clean
26
- FileUtils.rm_f(snapshot) if clean && File.exists?(snapshot.to_s)
27
- end
28
-
29
- private
30
-
31
- def image(name)
32
- Linecook.config[:roles][name.to_sym][:image]
33
- end
34
-
35
- def provider(name)
36
- provisioner = Linecook.config[:roles][name.to_sym][:provisioner] || Linecook.config[:provisioner][:default_provider]
37
- case provisioner
38
- when :chefzero
39
- Linecook::Chef
40
- when :packer
41
- Linecook::Packer
42
- else
43
- fail "Unsupported provisioner #{provisioner}"
44
- end
45
- end
46
- end
47
- end
@@ -1,82 +0,0 @@
1
- require 'json'
2
- require 'tempfile'
3
- require 'mkmf'
4
- require 'fileutils'
5
-
6
- require 'linecook-gem/util/executor'
7
-
8
-
9
- module Linecook
10
- module Packer
11
- def self.provision(build, role)
12
- Runner.new(build, role).run
13
- end
14
-
15
- class Runner
16
- SOURCE_URL = 'https://releases.hashicorp.com/packer/'
17
- PACKER_VERSION = '0.8.6'
18
- PACKER_PATH = File.join(Linecook::Config::LINECOOK_HOME, 'bin', 'packer')
19
-
20
- include Executor
21
-
22
- def initialize(build, role)
23
- role_config = Linecook.config[:roles][role.to_sym]
24
- @packer = packer_path
25
- @build = build
26
- @template = role_config[:template_path]
27
- end
28
-
29
- def run
30
- @build.start
31
- packer_template = inject_builder
32
- Tempfile.open('packer-linecook') do |template|
33
- template.write(JSON.dump(packer_template))
34
- template.flush
35
- execute("#{@packer} build #{template.path}", sudo: false)
36
- end
37
- end
38
-
39
- private
40
-
41
- def packer_path
42
- found = File.exists?(PACKER_PATH) ? PACKER_PATH : find_executable('packer')
43
- path = if found
44
- version = execute("#{found} --version", sudo: false, capture: true)
45
- Gem::Version.new(version) >= Gem::Version.new(PACKER_VERSION) ? found : nil
46
- end
47
-
48
- path ||= get_packer
49
- end
50
-
51
- def get_packer
52
- puts "packer too old (<#{PACKER_VERSION}) or not present, getting latest packer"
53
- arch = 1.size == 8 ? 'amd64' : '386'
54
- path = File.join(File.dirname(PACKER_PATH), 'packer.zip')
55
- url = File.join(SOURCE_URL, PACKER_VERSION, "packer_#{PACKER_VERSION}_#{Linecook::Config.platform}_#{arch}.zip")
56
- Linecook::Downloader.download(url, path)
57
- Linecook::Downloader.unzip(path)
58
- PACKER_PATH
59
- end
60
-
61
- def inject_builder
62
- packer_template = JSON.load(File.read(@template)).symbolize_keys
63
- packer_template.merge(builders: null_builder)
64
- end
65
-
66
- def null_builder
67
- [ communicator.merge(type: 'null') ]
68
- end
69
-
70
- def communicator
71
- {
72
- ssh_host: @build.ssh.hostname,
73
- ssh_username: @build.ssh.username,
74
- ssh_private_key_file: @build.ssh.keyfile,
75
- ssh_bastion_host: Linecook::Builder.ssh.hostname,
76
- ssh_bastion_username: Linecook::Builder.ssh.username,
77
- ssh_bastion_private_key_file: Linecook::Builder.ssh.keyfile,
78
- }
79
- end
80
- end
81
- end
82
- end
@@ -1,134 +0,0 @@
1
- require 'yaml'
2
- require 'json'
3
- require 'fileutils'
4
-
5
- require 'xhyve'
6
-
7
- module Linecook
8
- def self.config
9
- config = Config.load_config
10
- config
11
- end
12
-
13
- module Config
14
- extend self
15
- attr_reader :config
16
-
17
- LXC_MIN_VERSION = '1.1.4'
18
- CONFIG_PATH = File.join(Dir.pwd, 'linecook.yml').freeze # File.expand_path('../../../config/config.yml', __FILE__)
19
- SECRETS_PATH = File.join(Dir.pwd, 'secrets.ejson').freeze # File.expand_path('../../../config/config.yml', __FILE__)
20
- LINECOOK_HOME = File.expand_path('~/.linecook').freeze
21
- DEFAULT_CONFIG_PATH = File.join(LINECOOK_HOME, 'config.yml').freeze
22
- DEFAULT_CONFIG = {
23
- builder: {
24
- image: :live_image,
25
- name: 'builder',
26
- home: '/u/lxc',
27
- username: 'ubuntu',
28
- password: 'ubuntu'
29
- },
30
- provisioner: {
31
- default_provider: :chefzero,
32
- default_image: :base_image,
33
- chefzero: {
34
- audit: true
35
- }
36
- },
37
- image: {
38
- provider: {
39
- public: :github,
40
- private: :s3,
41
- },
42
- images: {
43
- live_iso: {
44
- name: 'livesys.iso',
45
- profile: :public,
46
- },
47
- live_image: {
48
- name: 'livesys.squashfs',
49
- profile: :public,
50
- },
51
- base_image: {
52
- name: 'ubuntu-base.squashfs',
53
- profile: :public,
54
- }
55
- }
56
- },
57
- packager: {
58
- provider: :ebs,
59
- ebs: {
60
- hvm: true,
61
- size: 10,
62
- region: 'us-east-1',
63
- copy_regions: [],
64
- account_ids: []
65
- }
66
- },
67
- roles: {
68
- }
69
- }
70
-
71
- def secrets
72
- @secrets ||= begin
73
- secrets_path = ENV['LINECOOK_SECRETS_PATH'] || SECRETS_PATH
74
- if File.exists?(secrets_path)
75
- ejson_path = File.join(Gem::Specification.find_by_name('ejson').gem_dir, 'build', "#{Linecook::Config.platform}-amd64", 'ejson' )
76
- command = "#{ejson_path} decrypt #{secrets_path}"
77
- secrets = JSON.load(`sudo #{command}`)
78
- secrets.deep_symbolize_keys
79
- else
80
- {}
81
- end
82
- end
83
- end
84
-
85
- def load_config
86
- @config ||= begin
87
- config_path = ENV['LINECOOK_CONFIG_PATH'] || CONFIG_PATH
88
- config = YAML.load(File.read(DEFAULT_CONFIG_PATH)) if File.exist?(DEFAULT_CONFIG_PATH)
89
- config.deep_merge!(YAML.load(File.read(config_path))) if File.exist?(config_path)
90
- (config || {}).deep_symbolize_keys!
91
- config.deep_merge!(secrets)
92
- end
93
- end
94
-
95
- def platform
96
- case RbConfig::CONFIG['host_os'].downcase
97
- when /linux/
98
- 'linux'
99
- when /darwin/
100
- 'darwin'
101
- else
102
- fail 'Linux and OS X are the only supported systems'
103
- end
104
- end
105
-
106
- private
107
-
108
- def setup
109
- FileUtils.mkdir_p(LINECOOK_HOME)
110
- config = {}
111
- config.merge!(YAML.load(File.read(DEFAULT_CONFIG_PATH))) if File.exist?(DEFAULT_CONFIG_PATH)
112
- File.write(DEFAULT_CONFIG_PATH, YAML.dump(DEFAULT_CONFIG.deep_merge(config)))
113
- check_perms if platform == 'darwin'
114
- check_lxc if platform == 'linux'
115
- end
116
-
117
- def check_lxc
118
- version = `lxc-info --version`
119
- fail "lxc too old (<#{LXC_MIN_VERSION}) or not present" unless Gem::Version.new(version) >= Gem::Version.new(LXC_MIN_VERSION)
120
- end
121
-
122
- def check_perms
123
- fix_perms if (File.stat(Xhyve::BINARY_PATH).uid != 0 || !File.stat(Xhyve::BINARY_PATH).setuid?)
124
- end
125
-
126
- def fix_perms
127
- puts "Xhyve requires root until https://github.com/mist64/xhyve/issues/60 is resolved\nPlease enter your sudo password to setuid on the xhyve binary"
128
- system("sudo chown root #{Xhyve::BINARY_PATH}")
129
- system("sudo chmod +s #{Xhyve::BINARY_PATH}")
130
- end
131
-
132
- setup
133
- end
134
- end
@@ -1,33 +0,0 @@
1
- module Linecook
2
- module Executor
3
- def capture(command, sudo: true)
4
- execute(command, sudo: sudo, capture: true)
5
- end
6
-
7
- def test(check)
8
- if @remote
9
- return @remote.test(check)
10
- else
11
- `#{check}`
12
- return $?.exitstatus == 0
13
- end
14
- end
15
-
16
- def execute(command, sudo: true, capture: false)
17
- command = "sudo #{command}" if sudo
18
- if @remote
19
- if capture
20
- return @remote.capture(command)
21
- else
22
- @remote.run(command)
23
- end
24
- else
25
- if capture
26
- return `#{command}`
27
- else
28
- system(command)
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,190 +0,0 @@
1
- require 'openssl'
2
-
3
- require 'sshkit'
4
- require 'sshkit/dsl'
5
- require 'net/ssh'
6
- require 'net/ssh/proxy/command'
7
-
8
- require 'linecook-gem/util/config'
9
-
10
- module Linecook
11
- class SSHKit::Formatter::Linecook < SSHKit::Formatter::Pretty
12
- def write_command(command)
13
- log_command_start(command) unless command.started?
14
- log_command_stdout(command) unless command.stdout.empty?
15
- log_command_stderr(command) unless command.stderr.empty?
16
- log_command_finished(command) if command.finished?
17
- end
18
-
19
- def log_command_start(command)
20
- print(command, 'run'.colorize(:green), command.to_s.colorize(:yellow))
21
- end
22
-
23
- def log_command_stdout(command)
24
- command.stdout.lines.each do |line|
25
- print(command, 'out'.colorize(:green), line)
26
- end
27
- command.stdout = ''
28
- end
29
-
30
- def log_command_stderr(command)
31
- command.stderr.lines.each do |line|
32
- print(command, 'err'.colorize(:yellow), line)
33
- end
34
- command.stderr = ''
35
- end
36
-
37
- def log_command_finished(command)
38
- if command.failure?
39
- print(command, 'failed'.colorize(:red), "with status #{command.exit_status} #{command.to_s.colorize(:yellow)} in #{sprintf('%5.3f seconds', command.runtime)}")
40
- else
41
- print(command, 'done'.colorize(:green), "#{command.to_s.colorize(:yellow)} in #{sprintf('%5.3f seconds', command.runtime)}")
42
- end
43
- end
44
-
45
- def print(command, state, message)
46
- line = "[#{command.host.to_s.colorize(:blue)}][#{state}] #{message}"
47
- line << "\n" unless line.end_with?("\n")
48
- original_output << line
49
- end
50
- end
51
-
52
- class SSH
53
- MAX_RETRIES = 5
54
- attr_reader :username, :password, :hostname, :keyfile
55
-
56
- def self.private_key
57
- userkey = File.expand_path("~/.ssh/id_rsa")
58
- dedicated_key = File.join(Linecook::Config::LINECOOK_HOME, 'linecook_ssh.pem')
59
- unless File.exists?(dedicated_key)
60
- File.write(dedicated_key, SSHKey.generate.private_key)
61
- FileUtils.chmod(0600, dedicated_key)
62
- end
63
- File.exists?(userkey) ? userkey : dedicated_key
64
- end
65
-
66
- def self.public_key(keyfile: nil)
67
- SSHKey.new(File.read(keyfile || private_key)).ssh_public_key
68
- end
69
-
70
- # Generate a fingerprint for an SSHv2 key used by amazon, yes, this is ugly
71
- def self.sshv2_fingerprint(key)
72
- _, blob = key.split(/ /)
73
- blob = blob.unpack("m*").first
74
- reader = Net::SSH::Buffer.new(blob)
75
- k=reader.read_key
76
- OpenSSL::Digest.new('md5',k.to_der).hexdigest.scan(/../).join(":")
77
- end
78
-
79
- def initialize(hostname, username: 'ubuntu', password: nil, keyfile: nil, proxy: nil, setup: true)
80
- @username = username
81
- @password = password
82
- @hostname = hostname
83
- @keyfile = keyfile
84
- @proxy = proxy_command(proxy) if proxy
85
- wait_for_connection
86
- setup_ssh_key if @keyfile && setup
87
- end
88
-
89
- def forward(local, remote:nil)
90
- remote ||= local
91
- opts = { password: @password, paranoid: Net::SSH::Verifiers::Null.new }
92
- opts.merge!({ proxy: @proxy }) if @proxy
93
- @session = Net::SSH.start(@hostname, @username, opts)
94
- @session.forward.remote(local, '127.0.0.1', remote)
95
- # Block to ensure it's open
96
- @session.loop { !@session.forward.active_remotes.include?([remote, '127.0.0.1']) }
97
- @keep_forwarding = true
98
- @forward = Thread.new do
99
- @session.loop(0.1) { @keep_forwarding }
100
- end
101
- end
102
-
103
- def stop_forwarding
104
- @keep_forwarding = false
105
- @forward.join
106
- @session.close unless @session.closed?
107
- end
108
-
109
- def test(check)
110
- result = nil
111
- on linecook_host do |_host|
112
- result = test(check)
113
- end
114
- result
115
- end
116
-
117
- def run(command)
118
- on linecook_host do |_host|
119
- execute(command)
120
- end
121
- end
122
-
123
- def capture(command)
124
- output = nil
125
- on linecook_host do |_host|
126
- output = capture(command)
127
- end
128
- output
129
- end
130
-
131
- def upload(data, path)
132
- on linecook_host do |_host|
133
- contents = File.exist?(data) ? data : StringIO.new(data)
134
- upload! contents, path
135
- end
136
- end
137
-
138
- def download(path, local: nil)
139
- on linecook_host do |_host|
140
- download! path, local || File.basename(path)
141
- end
142
- end
143
-
144
- private
145
-
146
- def wait_for_connection
147
- puts "Waiting for SSH connection"
148
- attempts = 0
149
- while attempts < MAX_RETRIES
150
- begin
151
- run("echo connected")
152
- return
153
- rescue SSHKit::Runner::ExecuteError
154
- puts "Retrying SSH connection"
155
- sleep(5)
156
- attempts += 1
157
- end
158
- end
159
- end
160
-
161
- def setup_ssh_key
162
- pubkey = Linecook::SSH.public_key(keyfile: @keyfile)
163
- config = Linecook.config[:builder]
164
- run("mkdir -p /home/#{config[:username]}/.ssh")
165
- upload(pubkey, "/home/#{config[:username]}/.ssh/authorized_keys")
166
- end
167
-
168
-
169
- def linecook_host
170
- @host ||= begin
171
- host = SSHKit::Host.new(user: @username, hostname: @hostname)
172
- host.password = @password if @password
173
- opts = {paranoid: Net::SSH::Verifiers::Null.new}
174
- opts.merge!({ proxy: @proxy }) if @proxy
175
- opts.merge!({ keys: [@keyfile], auth_methods: %w(publickey password) }) if @keyfile
176
- host.ssh_options = opts
177
- host
178
- end
179
- end
180
-
181
- def proxy_command(proxy)
182
- ssh_command = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no #{"-i #{@keyfile}" if @keyfile} #{proxy.username}@#{proxy.hostname} nc %h %p"
183
-
184
-
185
- Net::SSH::Proxy::Command.new(ssh_command)
186
- end
187
- end
188
- end
189
-
190
- SSHKit.config.output = SSHKit::Formatter::Linecook.new($stdout)