image_builder 0.0.1

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.
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