image_builder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rubocop.yml +16 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +32 -0
  7. data/Rakefile +29 -0
  8. data/bin/image_builder +234 -0
  9. data/image_builder.gemspec +30 -0
  10. data/lib/image_builder.rb +38 -0
  11. data/lib/image_builder/backends/backend_base.rb +10 -0
  12. data/lib/image_builder/backends/packer.rb +109 -0
  13. data/lib/image_builder/builders/aws_base.rb +101 -0
  14. data/lib/image_builder/builders/aws_chroot.rb +33 -0
  15. data/lib/image_builder/builders/aws_ebs.rb +15 -0
  16. data/lib/image_builder/builders/aws_instance.rb +45 -0
  17. data/lib/image_builder/builders/builder_base.rb +12 -0
  18. data/lib/image_builder/builders/null.rb +39 -0
  19. data/lib/image_builder/post_processors/compress.rb +24 -0
  20. data/lib/image_builder/post_processors/post_processors_base.rb +10 -0
  21. data/lib/image_builder/post_processors/vagrant.rb +41 -0
  22. data/lib/image_builder/provisioners/chef_base.rb +96 -0
  23. data/lib/image_builder/provisioners/chef_client.rb +115 -0
  24. data/lib/image_builder/provisioners/chef_solo.rb +33 -0
  25. data/lib/image_builder/provisioners/file.rb +26 -0
  26. data/lib/image_builder/provisioners/provisioner_base.rb +10 -0
  27. data/lib/image_builder/provisioners/shell.rb +47 -0
  28. data/lib/image_builder/version.rb +4 -0
  29. data/spec/aws_chroot_builder_spec.rb +30 -0
  30. data/spec/aws_ebs_builder_spec.rb +30 -0
  31. data/spec/aws_instance_builder_spec.rb +30 -0
  32. data/spec/chef_client_provisioner_spec.rb +32 -0
  33. data/spec/chef_solo_provisioner_spec.rb +28 -0
  34. data/spec/compress_postprocessor_spec.rb +15 -0
  35. data/spec/fixtures/test-knife.rb +40 -0
  36. data/spec/null_builder_spec.rb +21 -0
  37. data/spec/packer_backend_spec.rb +145 -0
  38. data/spec/vagrant_postprocessor_spec.rb +20 -0
  39. 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,10 @@
1
+ require_relative '../../image_builder.rb'
2
+
3
+ module ImageBuilder
4
+ module Provisioners
5
+ # Generic class doc comment
6
+ class Base
7
+ include ImageBuilder
8
+ end
9
+ end
10
+ 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,4 @@
1
+ # Generic top-level module comment
2
+ module ImageBuilder
3
+ VERSION = '0.0.1'
4
+ 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