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