image_builder 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rubocop.yml +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +29 -0
- data/bin/image_builder +234 -0
- data/image_builder.gemspec +30 -0
- data/lib/image_builder.rb +38 -0
- data/lib/image_builder/backends/backend_base.rb +10 -0
- data/lib/image_builder/backends/packer.rb +109 -0
- data/lib/image_builder/builders/aws_base.rb +101 -0
- data/lib/image_builder/builders/aws_chroot.rb +33 -0
- data/lib/image_builder/builders/aws_ebs.rb +15 -0
- data/lib/image_builder/builders/aws_instance.rb +45 -0
- data/lib/image_builder/builders/builder_base.rb +12 -0
- data/lib/image_builder/builders/null.rb +39 -0
- data/lib/image_builder/post_processors/compress.rb +24 -0
- data/lib/image_builder/post_processors/post_processors_base.rb +10 -0
- data/lib/image_builder/post_processors/vagrant.rb +41 -0
- data/lib/image_builder/provisioners/chef_base.rb +96 -0
- data/lib/image_builder/provisioners/chef_client.rb +115 -0
- data/lib/image_builder/provisioners/chef_solo.rb +33 -0
- data/lib/image_builder/provisioners/file.rb +26 -0
- data/lib/image_builder/provisioners/provisioner_base.rb +10 -0
- data/lib/image_builder/provisioners/shell.rb +47 -0
- data/lib/image_builder/version.rb +4 -0
- data/spec/aws_chroot_builder_spec.rb +30 -0
- data/spec/aws_ebs_builder_spec.rb +30 -0
- data/spec/aws_instance_builder_spec.rb +30 -0
- data/spec/chef_client_provisioner_spec.rb +32 -0
- data/spec/chef_solo_provisioner_spec.rb +28 -0
- data/spec/compress_postprocessor_spec.rb +15 -0
- data/spec/fixtures/test-knife.rb +40 -0
- data/spec/null_builder_spec.rb +21 -0
- data/spec/packer_backend_spec.rb +145 -0
- data/spec/vagrant_postprocessor_spec.rb +20 -0
- metadata +236 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative './provisioner_base.rb'
|
2
|
+
|
3
|
+
# rubocop:disable Style/EmptyLineBetweenDefs, Style/TrivialAccessors
|
4
|
+
|
5
|
+
module ImageBuilder
|
6
|
+
module Provisioners
|
7
|
+
module Chef
|
8
|
+
# Generic class doc comment
|
9
|
+
class Base < ImageBuilder::Provisioners::Base
|
10
|
+
attr_accessor :cookbook_paths
|
11
|
+
attr_accessor :config_template
|
12
|
+
attr_accessor :encrypted_data_bag_secret_path
|
13
|
+
attr_accessor :execute_command
|
14
|
+
attr_accessor :install_command
|
15
|
+
attr_accessor :json
|
16
|
+
attr_accessor :prevent_sudo
|
17
|
+
attr_accessor :run_list
|
18
|
+
attr_accessor :skip_install
|
19
|
+
attr_accessor :staging_directory
|
20
|
+
|
21
|
+
attr_reader :type
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@prevent_sudo = false
|
25
|
+
@skip_install = false
|
26
|
+
@encrypted_data_bag_secret_path = ENV['ENCRYPTED_DATA_BAG_SECRET']
|
27
|
+
end
|
28
|
+
|
29
|
+
# set attributes from something that looks like a knife.rb file
|
30
|
+
def self.from_file(path)
|
31
|
+
inst = new
|
32
|
+
@knife_file = ::File.expand_path(path)
|
33
|
+
|
34
|
+
# For knife hash params in knife.rb
|
35
|
+
knife = {} # rubocop:disable Lint/UselessAssignment
|
36
|
+
inst.instance_eval(IO.read(@knife_file))
|
37
|
+
|
38
|
+
inst
|
39
|
+
end
|
40
|
+
|
41
|
+
# Map knife.rb config options to their corresponding object attributes
|
42
|
+
# the :encrypted_data_bag_secret_path attribute is specific to the chef-solo
|
43
|
+
# provisioner, but we'll need it for all chef provisioners, since we need to
|
44
|
+
# upload it to the node before provisioning via the chef-client provisioner
|
45
|
+
def encrypted_data_bag_secret(sec)
|
46
|
+
@encrypted_data_bag_secret_path = sec
|
47
|
+
end
|
48
|
+
|
49
|
+
# While not used explicitly in the chef-client provisioner, this will give
|
50
|
+
# us consistent behavior across chef provisioners if we need to do a local
|
51
|
+
# cookbook lookup
|
52
|
+
def cookbook_path(path)
|
53
|
+
@cookbook_paths = [ENV['COOKBOOK_PATH']] << path
|
54
|
+
@cookbook_paths.flatten!
|
55
|
+
@cookbook_paths.compact!
|
56
|
+
@cookbook_paths.uniq!
|
57
|
+
end
|
58
|
+
|
59
|
+
def packer_hash
|
60
|
+
hash = { type: @type }
|
61
|
+
|
62
|
+
attr_to_hash(hash, :config_template)
|
63
|
+
attr_to_hash(hash, :execute_command)
|
64
|
+
attr_to_hash(hash, :install_command)
|
65
|
+
attr_to_hash(hash, :json)
|
66
|
+
attr_to_hash(hash, :prevent_sudo)
|
67
|
+
attr_to_hash(hash, :run_list)
|
68
|
+
attr_to_hash(hash, :skip_install)
|
69
|
+
attr_to_hash(hash, :staging_directory)
|
70
|
+
|
71
|
+
hash
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
attr_reader :knife_file
|
77
|
+
|
78
|
+
# These attributes might be found in a knife.rb file, but may not be used for
|
79
|
+
# a specific provisioner. This is only here to ensure the instance_eval in the
|
80
|
+
# from_file method (above) passes
|
81
|
+
def log_level(_lvl) end
|
82
|
+
def log_location(_loc) end
|
83
|
+
def node_name(_name) end
|
84
|
+
def client_key(_key) end
|
85
|
+
def validation_client_name(_client) end
|
86
|
+
def validation_key(_key) end
|
87
|
+
def chef_server_url(_url) end
|
88
|
+
def cache_type(_type) end
|
89
|
+
def cache_options(_opts) end
|
90
|
+
def cookbook_copyright(_x) end
|
91
|
+
def cookbook_email(_mail) end
|
92
|
+
def name(_name = nil) end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require_relative './chef_base.rb'
|
2
|
+
|
3
|
+
# rubocop:disable Style/TrivialAccessors
|
4
|
+
|
5
|
+
module ImageBuilder
|
6
|
+
module Provisioners
|
7
|
+
module Chef
|
8
|
+
# Generic class doc comment
|
9
|
+
class Client < ImageBuilder::Provisioners::Chef::Base
|
10
|
+
attr_accessor :chef_environment
|
11
|
+
attr_accessor :node_name
|
12
|
+
attr_accessor :server_url # required
|
13
|
+
attr_accessor :skip_clean_client
|
14
|
+
attr_accessor :skip_clean_node
|
15
|
+
attr_accessor :validation_client_name
|
16
|
+
attr_accessor :validation_key_path
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
super
|
20
|
+
@skip_clean_client = false
|
21
|
+
@skip_clean_node = false
|
22
|
+
@type = 'chef-client'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Map chef-client provisioner knife.rb config options
|
26
|
+
# to their corresponding object attributes
|
27
|
+
def node_name(name = nil)
|
28
|
+
@node_name = name unless name.nil? || name.strip.empty?
|
29
|
+
@node_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def chef_server_url(url)
|
33
|
+
@server_url = url
|
34
|
+
end
|
35
|
+
|
36
|
+
# Since this has an attr_accessor of the same name, need to
|
37
|
+
# write this as a getter & setter method
|
38
|
+
def validation_client_name(name = nil)
|
39
|
+
@validation_client_name = name unless name.nil?
|
40
|
+
@validation_client_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def validation_key(key)
|
44
|
+
@validation_key_path = fixup_key_path(key)
|
45
|
+
end
|
46
|
+
|
47
|
+
def packer_hash
|
48
|
+
hash = super
|
49
|
+
|
50
|
+
# Required attrs
|
51
|
+
attr_to_hash(hash, :server_url, true)
|
52
|
+
|
53
|
+
# Optional attrs
|
54
|
+
attr_to_hash(hash, :chef_environment)
|
55
|
+
attr_to_hash(hash, :node_name)
|
56
|
+
attr_to_hash(hash, :skip_clean_client)
|
57
|
+
attr_to_hash(hash, :skip_clean_node)
|
58
|
+
attr_to_hash(hash, :validation_client_name)
|
59
|
+
attr_to_hash(hash, :validation_key_path)
|
60
|
+
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# This may not come in as a valid file after reading knife.rb, try some fixups
|
67
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
68
|
+
def fixup_key_path(path)
|
69
|
+
return path if path.nil? || path.empty?
|
70
|
+
|
71
|
+
unless ::File.exist?(path)
|
72
|
+
file_name = ::File.basename(path)
|
73
|
+
sec_var = @encrypted_data_bag_secret_path
|
74
|
+
sec_env = ENV['ENCRYPTED_DATA_BAG_SECRET']
|
75
|
+
|
76
|
+
# Pearson-centric env vars
|
77
|
+
merle_key = ENV['MERLE_VALIDATOR']
|
78
|
+
path = check_file(merle_key) if valid_path?(merle_key)
|
79
|
+
|
80
|
+
merle = ENV['MERLE']
|
81
|
+
path = check_file(::File.join(merle, file_name)) if valid_path?(merle) && path.nil?
|
82
|
+
|
83
|
+
# 'standard' location checks
|
84
|
+
if valid_path?(@knife_file) && path.nil?
|
85
|
+
dir = ::File.dirname(@knife_file)
|
86
|
+
path = check_file(::File.join(dir, file_name))
|
87
|
+
end
|
88
|
+
|
89
|
+
if valid_path?(sec_var) && path.nil?
|
90
|
+
dir = ::File.dirname(sec_var)
|
91
|
+
path = check_file(::File.join(dir, file_name))
|
92
|
+
end
|
93
|
+
|
94
|
+
if valid_path?(sec_env) && path.nil?
|
95
|
+
dir = ::File.dirname(sec_env)
|
96
|
+
path = check_file(::File.join(dir, file_name))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
path
|
101
|
+
end
|
102
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
103
|
+
|
104
|
+
def check_file(path)
|
105
|
+
return path if ::File.exist?(path)
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def valid_path?(path)
|
110
|
+
!path.nil? && !path.strip.empty?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative './chef_base.rb'
|
2
|
+
|
3
|
+
# rubocop:disable Style/TrivialAccessors
|
4
|
+
|
5
|
+
module ImageBuilder
|
6
|
+
module Provisioners
|
7
|
+
module Chef
|
8
|
+
# Generic class doc comment
|
9
|
+
class Solo < ImageBuilder::Provisioners::Chef::Base
|
10
|
+
attr_accessor :data_bags_path
|
11
|
+
attr_accessor :remote_cookbook_paths
|
12
|
+
attr_accessor :roles_path
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@type = 'chef-solo'
|
17
|
+
end
|
18
|
+
|
19
|
+
def packer_hash
|
20
|
+
hash = super
|
21
|
+
|
22
|
+
attr_to_hash(hash, :data_bags_path)
|
23
|
+
attr_to_hash(hash, :remote_cookbook_paths)
|
24
|
+
attr_to_hash(hash, :roles_path)
|
25
|
+
attr_to_hash(hash, :cookbook_paths)
|
26
|
+
attr_to_hash(hash, :encrypted_data_bag_secret_path)
|
27
|
+
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative './provisioner_base.rb'
|
2
|
+
|
3
|
+
module ImageBuilder
|
4
|
+
module Provisioners
|
5
|
+
# Generic class doc comment
|
6
|
+
class File < ImageBuilder::Provisioners::Base
|
7
|
+
attr_accessor :source
|
8
|
+
attr_accessor :destination
|
9
|
+
|
10
|
+
attr_reader :type
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@type = 'file'
|
14
|
+
end
|
15
|
+
|
16
|
+
def packer_hash
|
17
|
+
hash = { type: @type }
|
18
|
+
|
19
|
+
attr_to_hash(hash, :source, true)
|
20
|
+
attr_to_hash(hash, :destination, true)
|
21
|
+
|
22
|
+
hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative './provisioner_base.rb'
|
2
|
+
|
3
|
+
module ImageBuilder
|
4
|
+
module Provisioners
|
5
|
+
# Generic class doc comment
|
6
|
+
class Shell < ImageBuilder::Provisioners::Base
|
7
|
+
# Only one of these is allowed/required
|
8
|
+
attr_accessor :inline
|
9
|
+
attr_accessor :script
|
10
|
+
attr_accessor :scripts
|
11
|
+
|
12
|
+
# Optional
|
13
|
+
attr_accessor :binary
|
14
|
+
attr_accessor :environment_vars
|
15
|
+
attr_accessor :execute_command
|
16
|
+
attr_accessor :inline_shebang
|
17
|
+
attr_accessor :remote_path
|
18
|
+
attr_accessor :start_retry_timeout
|
19
|
+
|
20
|
+
attr_reader :type
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@type = 'shell'
|
24
|
+
end
|
25
|
+
|
26
|
+
def packer_hash
|
27
|
+
hash = { type: @type }
|
28
|
+
|
29
|
+
# Let packer enforce the validation requirements
|
30
|
+
# for these attributes, don't make them required
|
31
|
+
attr_to_hash(hash, :inline)
|
32
|
+
attr_to_hash(hash, :script)
|
33
|
+
attr_to_hash(hash, :scripts)
|
34
|
+
|
35
|
+
# Optional attrs
|
36
|
+
attr_to_hash(hash, :binary)
|
37
|
+
attr_to_hash(hash, :environment_vars)
|
38
|
+
attr_to_hash(hash, :execute_command)
|
39
|
+
attr_to_hash(hash, :inline_shebang)
|
40
|
+
attr_to_hash(hash, :remote_path)
|
41
|
+
attr_to_hash(hash, :start_retry_timeout)
|
42
|
+
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../lib/image_builder/builders/aws_chroot.rb'
|
2
|
+
|
3
|
+
describe ImageBuilder::Builders::AWS::Chroot do
|
4
|
+
before(:each) do
|
5
|
+
@obj = ImageBuilder::Builders::AWS::Chroot.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'has the expected default block device mappings as a class attribute' do
|
9
|
+
expected = [
|
10
|
+
{ device_name: '/dev/sdf', virtual_name: 'ephemeral0' },
|
11
|
+
{ device_name: '/dev/sdg', virtual_name: 'ephemeral1' },
|
12
|
+
{ device_name: '/dev/sdh', virtual_name: 'ephemeral2' },
|
13
|
+
{ device_name: '/dev/sdi', virtual_name: 'ephemeral3' }
|
14
|
+
]
|
15
|
+
|
16
|
+
devs = ImageBuilder::Builders::AWS::Chroot.default_block_device_mappings
|
17
|
+
|
18
|
+
expect(devs).to eql expected
|
19
|
+
expect(devs[1][:device_name]).to eql '/dev/sdg'
|
20
|
+
expect(devs[1][:virtual_name]).to eql 'ephemeral1'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has the correct packer type' do
|
24
|
+
expect(@obj.type).to eql 'amazon-chroot'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'implements packer_hash()' do
|
28
|
+
expect(@obj.respond_to? :packer_hash).to be true
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../lib/image_builder/builders/aws_ebs.rb'
|
2
|
+
|
3
|
+
describe ImageBuilder::Builders::AWS::EBS do
|
4
|
+
before(:each) do
|
5
|
+
@obj = ImageBuilder::Builders::AWS::EBS.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'has the expected default block device mappings as a class attribute' do
|
9
|
+
expected = [
|
10
|
+
{ device_name: '/dev/sdf', virtual_name: 'ephemeral0' },
|
11
|
+
{ device_name: '/dev/sdg', virtual_name: 'ephemeral1' },
|
12
|
+
{ device_name: '/dev/sdh', virtual_name: 'ephemeral2' },
|
13
|
+
{ device_name: '/dev/sdi', virtual_name: 'ephemeral3' }
|
14
|
+
]
|
15
|
+
|
16
|
+
devs = ImageBuilder::Builders::AWS::EBS.default_block_device_mappings
|
17
|
+
|
18
|
+
expect(devs).to eql expected
|
19
|
+
expect(devs[1][:device_name]).to eql '/dev/sdg'
|
20
|
+
expect(devs[1][:virtual_name]).to eql 'ephemeral1'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has the correct packer type' do
|
24
|
+
expect(@obj.type).to eql 'amazon-ebs'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'implements packer_hash()' do
|
28
|
+
expect(@obj.respond_to? :packer_hash).to be true
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../lib/image_builder/builders/aws_instance.rb'
|
2
|
+
|
3
|
+
describe ImageBuilder::Builders::AWS::Instance do
|
4
|
+
before(:each) do
|
5
|
+
@obj = ImageBuilder::Builders::AWS::Instance.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'has the expected default block device mappings as a class attribute' do
|
9
|
+
expected = [
|
10
|
+
{ device_name: '/dev/sdf', virtual_name: 'ephemeral0' },
|
11
|
+
{ device_name: '/dev/sdg', virtual_name: 'ephemeral1' },
|
12
|
+
{ device_name: '/dev/sdh', virtual_name: 'ephemeral2' },
|
13
|
+
{ device_name: '/dev/sdi', virtual_name: 'ephemeral3' }
|
14
|
+
]
|
15
|
+
|
16
|
+
devs = ImageBuilder::Builders::AWS::Instance.default_block_device_mappings
|
17
|
+
|
18
|
+
expect(devs).to eql expected
|
19
|
+
expect(devs[1][:device_name]).to eql '/dev/sdg'
|
20
|
+
expect(devs[1][:virtual_name]).to eql 'ephemeral1'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has the correct packer type' do
|
24
|
+
expect(@obj.type).to eql 'amazon-instance'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'implements packer_hash()' do
|
28
|
+
expect(@obj.respond_to? :packer_hash).to be true
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative '../lib/image_builder/provisioners/chef_client.rb'
|
2
|
+
|
3
|
+
describe ImageBuilder::Provisioners::Chef::Client do
|
4
|
+
before(:each) do
|
5
|
+
@obj = ImageBuilder::Provisioners::Chef::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'has the expected default attributes' do
|
9
|
+
expect(@obj.prevent_sudo).to be false
|
10
|
+
expect(@obj.skip_install).to be false
|
11
|
+
expect(@obj.skip_clean_client).to be false
|
12
|
+
expect(@obj.skip_clean_node).to be false
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'loads the expected parameters from a file as a class method' do
|
16
|
+
# @obj.from_file(::File.join(::File.dirname(__FILE__), 'fixtures', 'test-knife.rb'))
|
17
|
+
obj = ImageBuilder::Provisioners::Chef::Client.from_file(::File.join(::File.dirname(__FILE__), 'fixtures', 'test-knife.rb'))
|
18
|
+
|
19
|
+
expect(obj.validation_client_name).to eql 'chef-validator'
|
20
|
+
expect(obj.validation_key_path).to eql '/dev/null'
|
21
|
+
expect(obj.server_url).to eql 'https://chef.chefserver.com/'
|
22
|
+
expect(obj.encrypted_data_bag_secret_path).to eql ENV['ENCRYPTED_DATA_BAG_SECRET']
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has the correct packer type' do
|
26
|
+
expect(@obj.type).to eql 'chef-client'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'implements packer_hash()' do
|
30
|
+
expect(@obj.respond_to? :packer_hash).to be true
|
31
|
+
end
|
32
|
+
end
|