require 'archive/tar/minitar' require 'zlib' require 'pathname' include Archive::Tar $:.unshift File.join(File.dirname(File.expand_path('../../..', __FILE__)), 'lib') $:.unshift File.join(File.dirname(File.expand_path('../../..', __FILE__)), 'features') require 'aws/codedeploy/local/deployer' LOCAL_DEPLOYMENT_GROUP_ID = 'test-local-deployments-folder' FILE_TO_POTENTIALLY_OVERWRITE = 'file-to-potentially-overwrite' BUNDLE_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE = 'BUNDLE_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE' ORIGINAL_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE = 'ORIGINAL_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE' Before("@codedeploy-local") do @test_directory = Dir.mktmpdir configure_local_agent(@test_directory) @appspec_filename = 'appspec.yml' end After("@codedeploy-local") do FileUtils.rm_rf(@test_directory) unless @test_directory.nil? end Given(/^I have a sample local directory_with_destination_files bundle with custom appspec filename ([^"]*)$/) do |appspec_filename| @bundle_original_directory_location = create_bundle_with_appspec_containing_source_and_destination_file(StepConstants::SAMPLE_APP_BUNDLE_FULL_PATH, appspec_filename) expect(File.directory?(@bundle_original_directory_location)).to be true @bundle_type = 'directory' @bundle_location = @bundle_original_directory_location @appspec_filename = appspec_filename expect(File.file?(@bundle_location)).to be false end Given(/^I have a sample local (tgz|tar|zip|zipped_directory|directory|relative_directory|custom_event_directory|directory_with_destination_files) bundle$/) do |bundle_type| case bundle_type when 'custom_event_directory' @bundle_original_directory_location = StepConstants::SAMPLE_CUSTOM_EVENT_APP_BUNDLE_FULL_PATH when 'directory_with_destination_files' @bundle_original_directory_location = create_bundle_with_appspec_containing_source_and_destination_file(StepConstants::SAMPLE_APP_BUNDLE_FULL_PATH) else @bundle_original_directory_location = StepConstants::SAMPLE_APP_BUNDLE_FULL_PATH end expect(File.directory?(@bundle_original_directory_location)).to be true @bundle_type = bundle_type.include?('zip') ? 'zip' : (bundle_type.include?('directory') ? 'directory' : bundle_type) case bundle_type when 'relative_directory' @bundle_location = Pathname.new(@bundle_original_directory_location).relative_path_from Pathname.getwd when 'zip' @bundle_location = zip_app_bundle(@test_directory) when 'zipped_directory' @bundle_location = zip_app_dir_bundle(@test_directory) when 'tar' @bundle_location = tar_app_bundle(@test_directory) when 'tgz' @bundle_location = tgz_app_bundle(@test_directory) else @bundle_location = @bundle_original_directory_location end expect(File.file?(@bundle_location)).to be true unless bundle_type.include? 'directory' end # Create a zip of the bundle with a nested directory containing the actual bundle files def zip_app_dir_bundle(temp_directory_to_create_bundle) zip_file_name = "#{temp_directory_to_create_bundle}/app_dir_bundle.zip" Dir.mktmpdir { |zip_build_dir| # Copy the bundle files into a temporary directory so we can just zip the sample bundle FileUtils.cp_r(StepConstants::SAMPLE_APP_BUNDLE_FULL_PATH, zip_build_dir) zip_directory(zip_build_dir, zip_file_name) } # Confirm appspec.yml is not at top level Zip::File.open(zip_file_name) { |zip| expect(zip.entries.map(&:name).include? "appspec.yml").to be false } zip_file_name end def tar_app_bundle(temp_directory_to_create_bundle) tar_file_name = "#{temp_directory_to_create_bundle}/app_bundle.tar" old_direcory = Dir.pwd #Unfortunately Minitar will keep pack all the file paths as given, so unless you change directories into the location where you want to pack the files the bundle won't have the correct files and folders Dir.chdir @bundle_original_directory_location File.open(tar_file_name, 'wb') { |tar| Minitar.pack(InstanceAgent::Plugins::CodeDeployPlugin::DeploymentCommandTracker.directories_and_files_inside(@bundle_original_directory_location), tar) } Dir.chdir old_direcory tar_file_name end def create_bundle_with_appspec_containing_source_and_destination_file(source_bundle_location, appspec_filename="appspec.yml") bundle_final_location = "#{@test_directory}/bundle_with_appspec_containing_source_and_destination_file" FileUtils.cp_r source_bundle_location, bundle_final_location # Remove the appspec file since we're going to overwrite it with a new one FileUtils.rm %W(#{bundle_final_location}/appspec.yml) # Read the default appspec.yml file File.open("#{source_bundle_location}/#{appspec_filename}", 'r') do |old_appspec| File.open("#{bundle_final_location}/#{appspec_filename}", 'w') do |new_appspec| # Create the new appspec in our bundle location but add the source and destination file lines old_appspec.each do |line| new_appspec << line if line.start_with?('os:') new_appspec.puts 'files:' new_appspec.puts " - source: #{FILE_TO_POTENTIALLY_OVERWRITE}" new_appspec.puts " destination: #{@test_directory}" end end end end bundle_final_location end Given(/^I have existing file in destination$/) do # Create the file to be copied in both our bundle and our destination for testing file overwrite behavior File.open("#{@bundle_location}/#{FILE_TO_POTENTIALLY_OVERWRITE}", 'w') {|f| f.write(BUNDLE_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE)} File.open("#{@test_directory}/#{FILE_TO_POTENTIALLY_OVERWRITE}", 'w') {|f| f.write(ORIGINAL_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE)} end def tgz_app_bundle(temp_directory_to_create_bundle) tgz_file_name = "#{temp_directory_to_create_bundle}/app_bundle.tgz" old_direcory = Dir.pwd #Unfortunately Minitar will keep pack all the file paths as given, so unless you change directories into the location where you want to pack the files the bundle won't have the correct files and folders Dir.chdir @bundle_original_directory_location File.open(tgz_file_name, 'wb') do |file| Zlib::GzipWriter.wrap(file) do |gz| Minitar.pack(InstanceAgent::Plugins::CodeDeployPlugin::DeploymentCommandTracker.directories_and_files_inside(@bundle_original_directory_location), gz) end end Dir.chdir old_direcory tgz_file_name end Given(/^I have a custom appspec filename ([^"]*)$/) do |custom_appspec_filename| @appspec_filename = custom_appspec_filename end When(/^I create a local deployment with my bundle with only events (.+)$/) do |custom_events| @local_deployment_succeeded = create_local_deployment(custom_events.split(' ')) end When(/^I create a local deployment with my bundle with file-exists-behavior (DISALLOW|OVERWRITE|RETAIN|MISSING)$/) do |file_exists_behavior| case file_exists_behavior when 'MISSING' @local_deployment_succeeded = create_local_deployment else @local_deployment_succeeded = create_local_deployment(nil, file_exists_behavior) end end When(/^I create a local deployment with my bundle$/) do @local_deployment_succeeded = create_local_deployment end def create_local_deployment(custom_events = nil, file_exists_behavior = nil) if (custom_events) codeedeploy_command_suffix = " --events #{custom_events.join(',')}" elsif (file_exists_behavior) codeedeploy_command_suffix = " --file-exists-behavior #{file_exists_behavior}" end # Windows doesn't respect shebang lines so ruby needs to be specified ruby_prefix_for_windows = StepConstants::IS_WINDOWS ? "ruby " : "" system "#{ruby_prefix_for_windows}bin/codedeploy-local --bundle-location #{@bundle_location} --type #{@bundle_type} --deployment-group #{LOCAL_DEPLOYMENT_GROUP_ID} --agent-configuration-file #{InstanceAgent::Config.config[:config_file]}#{codeedeploy_command_suffix} --appspec-filename #{@appspec_filename}" end Then(/^the local deployment command should succeed$/) do expect(@local_deployment_succeeded).to be true end Then(/^the local deployment command should fail$/) do expect(@local_deployment_succeeded).to be false end Then(/^the expected files \((\d+)\) should have have been locally deployed to my host(| twice)$/) do |expected_file_count, maybe_twice| deployment_ids = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentCommandTracker.directories_and_files_inside("#{InstanceAgent::Config.config[:root_dir]}/#{LOCAL_DEPLOYMENT_GROUP_ID}") step "the expected files (#{expected_file_count}) in directory #{bundle_original_directory_location}/scripts should have have been deployed#{maybe_twice} to my host during deployment with deployment group id #{LOCAL_DEPLOYMENT_GROUP_ID} and deployment ids #{deployment_ids.join(' ')}" end def bundle_original_directory_location #Sets default value if the original location was never set, such as with an s3 uploaded bundle @bundle_original_directory_location ||= StepConstants::SAMPLE_APP_BUNDLE_FULL_PATH @bundle_original_directory_location end Then(/^the scripts should have been executed during local deployment$/) do expected_executed_lifecycle_events = expected_executed_lifecycle_events_from_first_time_revision_is_deployed step "the scripts for events #{expected_executed_lifecycle_events.join(' ')} should have been executed and written to executed_proof_file in directory #{@test_directory}" end Then(/^the expected existing file should end up like file-exists-behavior (OVERWRITE|RETAIN) specifies$/) do |file_exists_behavior| file_to_potentially_overwrite_contents = IO.read("#{@test_directory}/#{FILE_TO_POTENTIALLY_OVERWRITE}") case file_exists_behavior when 'OVERWRITE' expect(file_to_potentially_overwrite_contents).to eq(BUNDLE_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE) when 'RETAIN' expect(file_to_potentially_overwrite_contents).to eq(ORIGINAL_FILE_CONTENT_TO_POTENTIALLY_OVERWRITE) end end def expected_executed_lifecycle_events_from_first_time_revision_is_deployed AWS::CodeDeploy::Local::Deployer::DEFAULT_ORDERED_LIFECYCLE_EVENTS - AWS::CodeDeploy::Local::Deployer::REQUIRED_LIFECYCLE_EVENTS - %w(BeforeBlockTraffic AfterBlockTraffic ApplicationStop) end Then(/^the scripts should have been executed during two local deployments$/) do expected_executed_lifecycle_events = expected_executed_lifecycle_events_from_first_time_revision_is_deployed + (AWS::CodeDeploy::Local::Deployer::DEFAULT_ORDERED_LIFECYCLE_EVENTS - AWS::CodeDeploy::Local::Deployer::REQUIRED_LIFECYCLE_EVENTS) step "the scripts for events #{expected_executed_lifecycle_events.join(' ')} should have been executed and written to executed_proof_file in directory #{@test_directory}" end Then(/^the scripts should have been executed during local deployment with only (.+)$/) do |custom_events| expected_executed_lifecycle_events = custom_events.split(' ') - %w(BeforeBlockTraffic AfterBlockTraffic ApplicationStop) step "the scripts for events #{expected_executed_lifecycle_events.join(' ')} should have been executed and written to executed_proof_file in directory #{@test_directory}" end