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