| Class | BoxGrinder::EBSPlugin |
| In: |
lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb
lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb |
| Parent: | BasePlugin |
| ROOT_DEVICE_NAME | = | '/dev/sda1' |
| POLL_FREQ | = | 1 |
| TIMEOUT | = | 1000 |
| EC2_HOSTNAME_LOOKUP_TIMEOUT | = | 10 |
| ROOT_DEVICE_NAME | = | '/dev/sda1' |
| POLL_FREQ | = | 1 |
| TIMEOUT | = | 1000 |
| EC2_HOSTNAME_LOOKUP_TIMEOUT | = | 10 |
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 223
223: def adjust_fstab(guestfs)
224: guestfs.sh("cat /etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > /etc/fstab.new")
225: guestfs.mv("/etc/fstab.new", "/etc/fstab")
226: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 223
223: def adjust_fstab(guestfs)
224: guestfs.sh("cat /etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > /etc/fstab.new")
225: guestfs.mv("/etc/fstab.new", "/etc/fstab")
226: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 67
67: def after_init
68: register_supported_os('fedora', ['13', '14', '15', '16'])
69: register_supported_os('rhel', ['6'])
70: register_supported_os('centos', ['5'])
71: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 67
67: def after_init
68: register_supported_os('fedora', ['13', '14', '15', '16'])
69: register_supported_os('rhel', ['6'])
70: register_supported_os('centos', ['5'])
71: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 171
171: def ami_by_name(name)
172: @ec2helper.ami_by_name(name, @plugin_config['account_number'])
173: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 171
171: def ami_by_name(name)
172: @ec2helper.ami_by_name(name, @plugin_config['account_number'])
173: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 232
232: def device_for_suffix(suffix)
233: return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}")
234: return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}")
235: nil
236: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 232
232: def device_for_suffix(suffix)
233: return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}")
234: return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}")
235: nil
236: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 207
207: def ebs_appliance_name
208: base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}"
209:
210: return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot']
211:
212: snapshot = 1
213:
214: while ami_by_name("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
215: snapshot += 1
216: end
217: # Reuse the last key (if there was one)
218: snapshot -=1 if snapshot > 1 and @plugin_config['overwrite']
219:
220: "#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}"
221: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 207
207: def ebs_appliance_name
208: base_path = "#{@appliance_config.name}/#{@appliance_config.os.name}/#{@appliance_config.os.version}/#{@appliance_config.version}.#{@appliance_config.release}"
209:
210: return "#{base_path}/#{@appliance_config.hardware.arch}" unless @plugin_config['snapshot']
211:
212: snapshot = 1
213:
214: while ami_by_name("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
215: snapshot += 1
216: end
217: # Reuse the last key (if there was one)
218: snapshot -=1 if snapshot > 1 and @plugin_config['overwrite']
219:
220: "#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}"
221: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 73
73: def execute
74: ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture"
75:
76: @log.debug "Checking if appliance is already registered..."
77: ami = ami_by_name(ebs_appliance_name)
78:
79: if ami and @plugin_config['overwrite']
80: @log.info "Overwrite is enabled. Stomping existing assets."
81: stomp_ebs(ami)
82: elsif ami
83: @log.warn "EBS AMI '#{ami.name}' is already registered as '#{ami.id}' (region: #{@current_region})."
84: return
85: end
86:
87: @log.info "Creating new EBS volume..."
88: size = 0
89: @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] }
90:
91: # create_volume, ceiling to avoid non-Integer values as per https://issues.jboss.org/browse/BGBUILD-224
92: volume = @ec2.volumes.create(:size => size.ceil.to_i, :availability_zone => @plugin_config['availability_zone'])
93:
94: @log.debug "Volume #{volume.id} created."
95: @log.debug "Waiting for EBS volume #{volume.id} to be available..."
96:
97: # wait for volume to be created
98: @ec2helper.wait_for_volume_status(:available, volume)
99:
100: # get first free device to mount the volume
101: suffix = free_device_suffix
102: device_name = "/dev/sd#{suffix}"
103: @log.trace "Got free device suffix: '#{suffix}'"
104:
105: @log.trace "Reading current instance id..."
106: # get_current_instance
107: current_instance = @ec2.instances[@current_instance_id]
108:
109: @log.trace "Got: #{current_instance.id}"
110: @log.info "Attaching created volume..."
111: # attach the volume to current host
112: volume.attach_to(current_instance, device_name)
113:
114: @log.debug "Waiting for EBS volume to be attached..."
115: # wait for volume to be attached
116: @ec2helper.wait_for_volume_status(:in_use, volume)
117:
118: @log.debug "Waiting for the attached EBS volume to be discovered by the OS"
119: wait_for_volume_attachment(suffix)
120:
121: @log.info "Copying data to EBS volume..."
122:
123: @image_helper.customize([@previous_deliverables.disk, device_for_suffix(suffix)], :automount => false) do |guestfs, guestfs_helper|
124: @image_helper.sync_filesystem(guestfs, guestfs_helper)
125:
126: @log.debug "Adjusting /etc/fstab..."
127: adjust_fstab(guestfs)
128: end
129:
130: @log.debug "Detaching EBS volume..."
131: volume.attachments.map(&:delete)
132:
133: @log.debug "Waiting for EBS volume to become available..."
134: @ec2helper.wait_for_volume_status(:available, volume)
135:
136: @log.info "Creating snapshot from EBS volume..."
137: snapshot = @ec2.snapshots.create(
138: :volume => volume,
139: :description => ebs_appliance_description)
140:
141: @log.debug "Waiting for snapshot #{snapshot.id} to be completed..."
142: @ec2helper.wait_for_snapshot_status(:completed, snapshot)
143:
144: @log.debug "Deleting temporary EBS volume..."
145: volume.delete
146:
147: @log.info "Registering image..."
148: image = @ec2.images.create(
149: :name => ebs_appliance_name,
150: :root_device_name => ROOT_DEVICE_NAME,
151: :block_device_mappings => { ROOT_DEVICE_NAME => {
152: :snapshot => snapshot,
153: :delete_on_termination => @plugin_config['delete_on_termination']
154: },
155: '/dev/sdb' => 'ephemeral0',
156: '/dev/sdc' => 'ephemeral1',
157: '/dev/sdd' => 'ephemeral2',
158: '/dev/sde' => 'ephemeral3'},
159: :architecture => @appliance_config.hardware.base_arch,
160: :kernel_id => @ec2_endpoints[@current_region][:kernel][@appliance_config.hardware.base_arch.intern][:aki],
161: :description => ebs_appliance_description)
162:
163: @log.info "Waiting for the new EBS AMI to become available"
164: @ec2helper.wait_for_image_state(:available, image)
165: @log.info "EBS AMI '#{image.name}' registered: #{image.id} (region: #{@current_region})"
166: rescue Timeout::Error
167: @log.error "An operation timed out. Manual intervention may be necessary to complete the task."
168: raise
169: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 73
73: def execute
74: ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture"
75:
76: @log.debug "Checking if appliance is already registered..."
77: ami = ami_by_name(ebs_appliance_name)
78:
79: if ami and @plugin_config['overwrite']
80: @log.info "Overwrite is enabled. Stomping existing assets."
81: stomp_ebs(ami)
82: elsif ami
83: @log.warn "EBS AMI '#{ami.name}' is already registered as '#{ami.id}' (region: #{@current_region})."
84: return
85: end
86:
87: @log.info "Creating new EBS volume..."
88: size = 0
89: @appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] }
90:
91: # create_volume, ceiling to avoid non-Integer values as per https://issues.jboss.org/browse/BGBUILD-224
92: volume = @ec2.volumes.create(:size => size.ceil.to_i, :availability_zone => @plugin_config['availability_zone'])
93:
94: @log.debug "Volume #{volume.id} created."
95: @log.debug "Waiting for EBS volume #{volume.id} to be available..."
96:
97: # wait for volume to be created
98: @ec2helper.wait_for_volume_status(:available, volume)
99:
100: # get first free device to mount the volume
101: suffix = free_device_suffix
102: device_name = "/dev/sd#{suffix}"
103: @log.trace "Got free device suffix: '#{suffix}'"
104:
105: @log.trace "Reading current instance id..."
106: # get_current_instance
107: current_instance = @ec2.instances[@current_instance_id]
108:
109: @log.trace "Got: #{current_instance.id}"
110: @log.info "Attaching created volume..."
111: # attach the volume to current host
112: volume.attach_to(current_instance, device_name)
113:
114: @log.debug "Waiting for EBS volume to be attached..."
115: # wait for volume to be attached
116: @ec2helper.wait_for_volume_status(:in_use, volume)
117:
118: @log.debug "Waiting for the attached EBS volume to be discovered by the OS"
119: wait_for_volume_attachment(suffix)
120:
121: @log.info "Copying data to EBS volume..."
122:
123: @image_helper.customize([@previous_deliverables.disk, device_for_suffix(suffix)], :automount => false) do |guestfs, guestfs_helper|
124: @image_helper.sync_filesystem(guestfs, guestfs_helper)
125:
126: @log.debug "Adjusting /etc/fstab..."
127: adjust_fstab(guestfs)
128: end
129:
130: @log.debug "Detaching EBS volume..."
131: volume.attachments.map(&:delete)
132:
133: @log.debug "Waiting for EBS volume to become available..."
134: @ec2helper.wait_for_volume_status(:available, volume)
135:
136: @log.info "Creating snapshot from EBS volume..."
137: snapshot = @ec2.snapshots.create(
138: :volume => volume,
139: :description => ebs_appliance_description)
140:
141: @log.debug "Waiting for snapshot #{snapshot.id} to be completed..."
142: @ec2helper.wait_for_snapshot_status(:completed, snapshot)
143:
144: @log.debug "Deleting temporary EBS volume..."
145: volume.delete
146:
147: @log.info "Registering image..."
148: image = @ec2.images.create(
149: :name => ebs_appliance_name,
150: :root_device_name => ROOT_DEVICE_NAME,
151: :block_device_mappings => { ROOT_DEVICE_NAME => {
152: :snapshot => snapshot,
153: :delete_on_termination => @plugin_config['delete_on_termination']
154: },
155: '/dev/sdb' => 'ephemeral0',
156: '/dev/sdc' => 'ephemeral1',
157: '/dev/sdd' => 'ephemeral2',
158: '/dev/sde' => 'ephemeral3'},
159: :architecture => @appliance_config.hardware.base_arch,
160: :kernel_id => @ec2_endpoints[@current_region][:kernel][@appliance_config.hardware.base_arch.intern][:aki],
161: :description => ebs_appliance_description)
162:
163: @log.info "Waiting for the new EBS AMI to become available"
164: @ec2helper.wait_for_image_state(:available, image)
165: @log.info "EBS AMI '#{image.name}' registered: #{image.id} (region: #{@current_region})"
166: rescue Timeout::Error
167: @log.error "An operation timed out. Manual intervention may be necessary to complete the task."
168: raise
169: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 238
238: def free_device_suffix
239: ("f".."p").each do |suffix|
240: return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}")
241: end
242: raise "Found too many attached devices. Cannot attach EBS volume."
243: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 238
238: def free_device_suffix
239: ("f".."p").each do |suffix|
240: return suffix unless File.exists?("/dev/sd#{suffix}") or File.exists?("/dev/xvd#{suffix}")
241: end
242: raise "Found too many attached devices. Cannot attach EBS volume."
243: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 182
182: def stomp_ebs(ami)
183: #Find any instances that are running, if they are not stopped then abort.
184: if live = @ec2helper.live_instances(ami)
185: if @plugin_config['terminate_instances']
186: @log.info "Terminating the following instances: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
187: terminate_instances(live)
188: else
189: raise "There are still instances of #{ami.id} running, you should terminate them after " <<
190: "preserving any important data: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
191: end
192: end
193:
194: @log.info("Finding the primary snapshot associated with #{ami.id}.")
195: primary_snapshot = @ec2helper.snapshot_by_id(ami.block_device_mappings[ami.root_device_name].snapshot_id)
196:
197: @log.info("De-registering the EBS AMI.")
198: ami.deregister
199: @ec2helper.wait_for_image_death(ami)
200:
201: if !@plugin_config['preserve_snapshots'] and primary_snapshot
202: @log.info("Deleting the primary snapshot.")
203: primary_snapshot.delete
204: end
205: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 182
182: def stomp_ebs(ami)
183: #Find any instances that are running, if they are not stopped then abort.
184: if live = @ec2helper.live_instances(ami)
185: if @plugin_config['terminate_instances']
186: @log.info "Terminating the following instances: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
187: terminate_instances(live)
188: else
189: raise "There are still instances of #{ami.id} running, you should terminate them after " <<
190: "preserving any important data: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
191: end
192: end
193:
194: @log.info("Finding the primary snapshot associated with #{ami.id}.")
195: primary_snapshot = @ec2helper.snapshot_by_id(ami.block_device_mappings[ami.root_device_name].snapshot_id)
196:
197: @log.info("De-registering the EBS AMI.")
198: ami.deregister
199: @ec2helper.wait_for_image_death(ami)
200:
201: if !@plugin_config['preserve_snapshots'] and primary_snapshot
202: @log.info("Deleting the primary snapshot.")
203: primary_snapshot.delete
204: end
205: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 175
175: def terminate_instances(instances)
176: instances.map(&:terminate)
177: instances.each do |i|
178: @ec2helper.wait_for_instance_death(i)
179: end
180: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 175
175: def terminate_instances(instances)
176: instances.map(&:terminate)
177: instances.each do |i|
178: @ec2helper.wait_for_instance_death(i)
179: end
180: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 245
245: def valid_platform?
246: begin
247: region = EC2Helper::availability_zone_to_region(EC2Helper::current_availability_zone)
248: return true if @ec2_endpoints.has_key? region
249: @log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{@ec2_endpoints.join(", ")}"
250: rescue Net::HTTPServerException => e
251: @log.warn "An error was returned when attempting to retrieve the ec2 hostname: #{e.to_s}"
252: rescue Timeout::Error => t
253: @log.warn "A timeout occurred while attempting to retrieve the ec2 hostname: #{t.to_s}"
254: end
255: false
256: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 245
245: def valid_platform?
246: begin
247: region = EC2Helper::availability_zone_to_region(EC2Helper::current_availability_zone)
248: return true if @ec2_endpoints.has_key? region
249: @log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{@ec2_endpoints.join(", ")}"
250: rescue Net::HTTPServerException => e
251: @log.warn "An error was returned when attempting to retrieve the ec2 hostname: #{e.to_s}"
252: rescue Timeout::Error => t
253: @log.warn "A timeout occurred while attempting to retrieve the ec2 hostname: #{t.to_s}"
254: end
255: false
256: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 35
35: def validate
36: @ec2_endpoints = EC2Helper::endpoints
37:
38: raise PluginValidationError, "You are trying to run this plugin on an invalid platform. You can run the EBS delivery plugin only on EC2." unless valid_platform?
39:
40: @current_availability_zone = EC2Helper::current_availability_zone
41: @current_instance_id = EC2Helper::current_instance_id
42: @current_region = EC2Helper::availability_zone_to_region(@current_availability_zone)
43:
44: set_default_config_value('availability_zone', @current_availability_zone)
45: set_default_config_value('delete_on_termination', true)
46: set_default_config_value('overwrite', false)
47: set_default_config_value('snapshot', false)
48: set_default_config_value('preserve_snapshots', false)
49: set_default_config_value('terminate_instances', false)
50: validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin')
51:
52: raise PluginValidationError, "You can only convert to EBS type AMI appliances converted to EC2 format. Use '-p ec2' switch. For more info about EC2 plugin see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EC2_Platform_Plugin." unless @previous_plugin_info[:name] == :ec2
53: raise PluginValidationError, "You selected #{@plugin_config['availability_zone']} availability zone, but your instance is running in #{@current_availability_zone} zone. Please change availability zone in plugin configuration file to #{@current_availability_zone} (see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin) or use another instance in #{@plugin_config['availability_zone']} zone to create your EBS AMI." if @plugin_config['availability_zone'] != @current_availability_zone
54:
55: @plugin_config['account_number'].to_s.gsub!(/-/, '')
56:
57: AWS.config(:access_key_id => @plugin_config['access_key'],
58: :secret_access_key => @plugin_config['secret_access_key'],
59: :ec2_endpoint => @ec2_endpoints[@current_region][:endpoint],
60: :max_retries => 5,
61: :use_ssl => @plugin_config['use_ssl'])
62:
63: @ec2 = AWS::EC2.new
64: @ec2helper = EC2Helper.new(@ec2, :log => @log)
65: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 35
35: def validate
36: @ec2_endpoints = EC2Helper::endpoints
37:
38: raise PluginValidationError, "You are trying to run this plugin on an invalid platform. You can run the EBS delivery plugin only on EC2." unless valid_platform?
39:
40: @current_availability_zone = EC2Helper::current_availability_zone
41: @current_instance_id = EC2Helper::current_instance_id
42: @current_region = EC2Helper::availability_zone_to_region(@current_availability_zone)
43:
44: set_default_config_value('availability_zone', @current_availability_zone)
45: set_default_config_value('delete_on_termination', true)
46: set_default_config_value('overwrite', false)
47: set_default_config_value('snapshot', false)
48: set_default_config_value('preserve_snapshots', false)
49: set_default_config_value('terminate_instances', false)
50: validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin')
51:
52: raise PluginValidationError, "You can only convert to EBS type AMI appliances converted to EC2 format. Use '-p ec2' switch. For more info about EC2 plugin see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EC2_Platform_Plugin." unless @previous_plugin_info[:name] == :ec2
53: raise PluginValidationError, "You selected #{@plugin_config['availability_zone']} availability zone, but your instance is running in #{@current_availability_zone} zone. Please change availability zone in plugin configuration file to #{@current_availability_zone} (see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin) or use another instance in #{@plugin_config['availability_zone']} zone to create your EBS AMI." if @plugin_config['availability_zone'] != @current_availability_zone
54:
55: @plugin_config['account_number'].to_s.gsub!(/-/, '')
56:
57: AWS.config(:access_key_id => @plugin_config['access_key'],
58: :secret_access_key => @plugin_config['secret_access_key'],
59: :ec2_endpoint => @ec2_endpoints[@current_region][:endpoint],
60: :max_retries => 5,
61: :use_ssl => @plugin_config['use_ssl'])
62:
63: @ec2 = AWS::EC2.new
64: @ec2helper = EC2Helper.new(@ec2, :log => @log)
65: end
# File lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb, line 228
228: def wait_for_volume_attachment(suffix)
229: @ec2helper.wait_with_timeout(POLL_FREQ, TIMEOUT){ device_for_suffix(suffix) != nil }
230: end