From 8bc7f07f9c73887b9135d18c97aa352f676b6162 Mon Sep 17 00:00:00 2001 From: Anandaraju Coimbatore Sivaraju Date: Wed, 22 Jul 2020 15:19:43 -0700 Subject: [PATCH] [Agent 1.1.2] Add unit test to verify unpack_bundle ruby unzip mechanism Why is this change needed? --------- Prior to this change, we didn't have a way to test the "overwrite" logic in ruby unzip during the fallback mechanism of unpack_bundle method in DownloadBundle command. How does it address the issue? --------- This change adds a unit test to verify if the fallback mechanism works with a debris file in the deployment archive directory. --- .../plugins/codedeploy/unpack_bundle_test.rb | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 test/instance_agent/plugins/codedeploy/unpack_bundle_test.rb diff --git a/test/instance_agent/plugins/codedeploy/unpack_bundle_test.rb b/test/instance_agent/plugins/codedeploy/unpack_bundle_test.rb new file mode 100644 index 00000000..ad55f47d --- /dev/null +++ b/test/instance_agent/plugins/codedeploy/unpack_bundle_test.rb @@ -0,0 +1,121 @@ +require 'test_helper' +require 'certificate_helper' +require 'stringio' +require 'aws/codedeploy/local/deployer' + +class UnpackBundleTest < InstanceAgentTestCase + include InstanceAgent::Plugins::CodeDeployPlugin + def generate_signed_message_for(map) + message = @cert_helper.sign_message(map.to_json) + spec = OpenStruct.new({ :payload => message }) + spec.format = "PKCS7/JSON" + + return spec + end + + # method to create a local source bundle file as zip + def setup_local_file_bundle + @local_file_directory = File.join(@root_dir, @deployment_group_id.to_s, 'LocalFileDirectory') + FileUtils.rm_rf(@local_file_directory) + FileUtils.mkdir_p(@local_file_directory) + + input_filenames = %w(file1.txt file2.txt) + input_filenames.each do |filename| + File.open(File.join(@local_file_directory, filename), "w") do |f| + f.write("content of #{filename}") + f.close + end + end + # create the bundle as a local zip file + @local_file_location = File.join(@local_file_directory, "bundle.zip") + Zip::File.open(@local_file_location, Zip::File::CREATE) do |zipfile| + input_filenames.each do |filename| + zipfile.add(filename, File.join(@local_file_directory, filename)) + end + end + end + + context 'The CodeDeploy Plugin Command Executor' do + setup do + @test_hook_mapping = { + "BeforeBlockTraffic"=>["BeforeBlockTraffic"], + "AfterBlockTraffic"=>["AfterBlockTraffic"], + "ApplicationStop"=>["ApplicationStop"], + "BeforeInstall"=>["BeforeInstall"], + "AfterInstall"=>["AfterInstall"], + "ApplicationStart"=>["ApplicationStart"], + "BeforeAllowTraffic"=>["BeforeAllowTraffic"], + "AfterAllowTraffic"=>["AfterAllowTraffic"], + "ValidateService"=>["ValidateService"] + } + @deploy_control_client = mock + @command_executor = InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor.new( + { + :deploy_control_client => @deploy_control_client, + :hook_mapping => @test_hook_mapping + }) + @aws_region = 'us-east-1' + InstanceMetadata.stubs(:region).returns(@aws_region) + end + + context "when executing a command" do + setup do + @cert_helper = CertificateHelper.new + @deployment_id = SecureRandom.uuid + @deployment_group_name = "TestDeploymentGroup" + @application_name = "TestApplicationName" + @deployment_group_id = "foobar" + @command = Aws::CodeDeployCommand::Types::HostCommandInstance.new( + :host_command_identifier => "command-1", + :deployment_execution_id => "test-execution") + @root_dir = '/tmp/codedeploy/' + @deployment_root_dir = File.join(@root_dir, @deployment_group_id.to_s, @deployment_id.to_s) + @archive_root_dir = File.join(@deployment_root_dir, 'deployment-archive') + ProcessManager::Config.config[:root_dir] = @root_dir + end + + context "test fallback mechanism in unpack_bundle in DownloadBundle" do + setup do + setup_local_file_bundle + + # Create a debris file in the deployment-archive directory to simulate Zip::DestinationFileExistsError. + # This error will be thrown, if the ruby unzip overwrite option is not enabled and when the @archive_root_dir already has the same file. + # With the ruby unzip overwrite fix, the unpack_bundle should succeed even with debris files. + FileUtils.rm_rf(@archive_root_dir) + FileUtils.mkdir_p(@archive_root_dir) + FileUtils.cp(File.join(@local_file_directory, 'file1.txt'), @archive_root_dir) + + # We need to avoid removing @archive_root_dir in the actual logic, to avoid debris file to be deleted. + FileUtils.stubs(:rm_rf).with(@archive_root_dir) + # This exception will let the unpack_bundle method to use the rubyzip fallback mechanism + InstanceAgent::LinuxUtil.expects(:extract_zip) + .with(File.join(@deployment_root_dir, 'bundle.tar'), @archive_root_dir) + .raises("Exception: System unzip throws exception with non-zero exit code") + + @command.command_name = 'DownloadBundle' + @bundle_type = 'zip' + @deployment_spec = generate_signed_message_for( + { + "DeploymentId" => @deployment_id.to_s, + "DeploymentGroupId" => @deployment_group_id.to_s, + "ApplicationName" => @application_name, + "DeploymentGroupName" => @deployment_group_name, + "Revision" => { + "RevisionType" => "Local File", + "LocalRevision" => { + "Location" => @local_file_location, + "BundleType" => @bundle_type + } + } + }) + end + + should 'execute DownloadBundle command with debris file in deployment-archive' do + assert_equal 1, (Dir.entries(@archive_root_dir) - [".", ".."]).size + @command_executor.execute_command(@command, @deployment_spec) + assert_equal 2, (Dir.entries(@archive_root_dir) - [".", ".."]).size + end + end + end + end +end