diff --git a/lib/instance_agent/plugins/codedeploy/command_executor.rb b/lib/instance_agent/plugins/codedeploy/command_executor.rb index 7f1a203..5d7e198 100644 --- a/lib/instance_agent/plugins/codedeploy/command_executor.rb +++ b/lib/instance_agent/plugins/codedeploy/command_executor.rb @@ -222,7 +222,30 @@ def create_hook_executor(lifecycle_event, deployment_spec) :deployment_root_dir => deployment_root_dir(deployment_spec), :last_successful_deployment_dir => last_successful_deployment_dir(deployment_spec.deployment_group_id), :most_recent_deployment_dir => most_recent_deployment_dir(deployment_spec.deployment_group_id), - :app_spec_path => deployment_spec.app_spec_path) + :app_spec_path => deployment_spec.app_spec_path, + :revision_envs => get_revision_envs(deployment_spec)) + end + + private + def get_revision_envs(deployment_spec) + case deployment_spec.revision_source + when 'S3' + return get_s3_envs(deployment_spec) + when 'GitHub', 'Local File', 'Local Directory' + return {} + else + raise "Unknown revision type '#{deployment_spec.revision_source}'" + end + end + + private + def get_s3_envs(deployment_spec) + return { + "BUNDLE_BUCKET" => deployment_spec.bucket, + "BUNDLE_KEY" => deployment_spec.key, + "BUNDLE_VERSION" => deployment_spec.version, + "BUNDLE_ETAG" => deployment_spec.etag + } end private diff --git a/lib/instance_agent/plugins/codedeploy/hook_executor.rb b/lib/instance_agent/plugins/codedeploy/hook_executor.rb index ae1608f..acc769d 100644 --- a/lib/instance_agent/plugins/codedeploy/hook_executor.rb +++ b/lib/instance_agent/plugins/codedeploy/hook_executor.rb @@ -106,6 +106,7 @@ def initialize(arguments = {}) 'APPLICATION_NAME' => @application_name, 'DEPLOYMENT_GROUP_NAME' => @deployment_group_name, 'DEPLOYMENT_GROUP_ID' => @deployment_group_id} + @child_envs.merge!(arguments[:revision_envs]) if arguments[:revision_envs] end def is_noop? diff --git a/test/instance_agent/plugins/codedeploy/command_executor_test.rb b/test/instance_agent/plugins/codedeploy/command_executor_test.rb index e81a050..a10a983 100644 --- a/test/instance_agent/plugins/codedeploy/command_executor_test.rb +++ b/test/instance_agent/plugins/codedeploy/command_executor_test.rb @@ -16,6 +16,15 @@ def generate_signed_message_for(map) return spec end + def s3_env_vars() + return { + "BUNDLE_BUCKET" => @s3Revision["Bucket"], + "BUNDLE_KEY" => @s3Revision["Key"], + "BUNDLE_VERSION" => @s3Revision["Version"], + "BUNDLE_ETAG" => @s3Revision["Etag"] + } + end + context 'The CodeDeploy Plugin Command Executor' do setup do @test_hook_mapping = { "BeforeBlockTraffic"=>["BeforeBlockTraffic"], @@ -711,7 +720,8 @@ def generate_signed_message_for(map) :deployment_root_dir => @deployment_root_dir, :last_successful_deployment_dir => nil, :most_recent_deployment_dir => nil, - :app_spec_path => 'appspec.yml'} + :app_spec_path => 'appspec.yml', + :revision_envs => s3_env_vars()} @mock_hook_executor = mock end @@ -906,7 +916,8 @@ def generate_signed_message_for(map) :deployment_type => @deployment_type, :last_successful_deployment_dir => nil, :most_recent_deployment_dir => nil, - :app_spec_path => 'appspec.yml'} + :app_spec_path => 'appspec.yml', + :revision_envs => s3_env_vars()} @hook_executor_constructor_hash_1 = hook_executor_constructor_hash.merge({:lifecycle_event => "lifecycle_event_1"}) @hook_executor_constructor_hash_2 = hook_executor_constructor_hash.merge({:lifecycle_event => "lifecycle_event_2"}) @mock_hook_executor = mock diff --git a/test/instance_agent/plugins/codedeploy/hook_executor_test.rb b/test/instance_agent/plugins/codedeploy/hook_executor_test.rb index d685b5c..2b24c18 100644 --- a/test/instance_agent/plugins/codedeploy/hook_executor_test.rb +++ b/test/instance_agent/plugins/codedeploy/hook_executor_test.rb @@ -6,7 +6,7 @@ class HookExecutorTest < InstanceAgentTestCase include InstanceAgent::Plugins::CodeDeployPlugin - def create_full_hook_executor + def create_hook_executor(map = {}) HookExecutor.new ({:lifecycle_event => @lifecycle_event, :application_name => @application_name, :deployment_id => @deployment_id, @@ -17,7 +17,8 @@ def create_full_hook_executor :deployment_root_dir => @deployment_root_dir, :last_successful_deployment_dir => @last_successful_deployment_dir, :most_recent_deployment_dir => @most_recent_deployment_dir, - :app_spec_path => @app_spec_path}) + :app_spec_path => @app_spec_path} + .merge(map)) end context "testing hook executor" do @@ -91,13 +92,13 @@ def create_full_hook_executor should "fail if app spec not found" do File.stubs(:exists?).with(){|value| value.is_a?(String) && value.end_with?("/app_spec")}.returns(false) assert_raised_with_message("The CodeDeploy agent did not find an AppSpec file within the unpacked revision directory at revision-relative path \"app_spec\". The revision was unpacked to directory \"deployment/root/dir/deployment-archive\", and the AppSpec file was expected but not found at path \"deployment/root/dir/deployment-archive/app_spec\". Consult the AWS CodeDeploy Appspec documentation for more information at http://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file.html", RuntimeError)do - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end end should "parse an app spec from the current deployments directory" do File.expects(:read).with(File.join(@deployment_root_dir, 'deployment-archive', @app_spec_path)) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end context "hook is before download bundle" do @@ -107,7 +108,7 @@ def create_full_hook_executor should "parse an app spec from the last successful deployment's directory" do File.expects(:read).with(File.join(@last_successful_deployment_dir, 'deployment-archive', @app_spec_path)) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end end @@ -118,7 +119,7 @@ def create_full_hook_executor should "parse an app spec from the last successful deployment's directory" do File.expects(:read).with(File.join(@last_successful_deployment_dir, 'deployment-archive', @app_spec_path)) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end end @@ -131,7 +132,7 @@ def create_full_hook_executor should "parse an app spec from the most recent deployment's directory" do File.expects(:read).with(File.join(@most_recent_deployment_dir, 'deployment-archive', @app_spec_path)) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end end end @@ -152,7 +153,7 @@ def create_full_hook_executor setup do @app_spec = {"version" => 0.0, "os" => "linux", "hooks" => {}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end should "do nothing" do @@ -169,7 +170,7 @@ def create_full_hook_executor @app_spec = {"version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=>[{'location'=>'test'}]}} YAML.stubs(:load).returns(@app_spec) @script_location = File.join(@deployment_root_dir, 'deployment-archive', 'test') - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end should "not be a noop" do @@ -241,11 +242,25 @@ def create_full_hook_executor InstanceAgent::ThreadJoiner.stubs(:new).returns(@thread_joiner) end + context "extra child environment variables are added" do + setup do + revision_envs = {"TEST_ENVIRONMENT_VARIABLE" => "ONE", "ANOTHER_ENV_VARIABLE" => "TWO"} + @child_env.merge!(revision_envs) + @hook_executor = create_hook_executor(:revision_envs => revision_envs) + end + + should "call popen with the environment variables" do + Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr]) + @value.stubs(:exitstatus).returns(0) + @hook_executor.execute() + end + end + context 'scripts fail for unknown reason' do setup do @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test", "timeout"=>"30"}]}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor @popen_error = Errno::ENOENT Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).raises(@popen_error, 'su') end @@ -262,7 +277,7 @@ def create_full_hook_executor setup do @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test", "timeout"=>"30"}]}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor @thread_joiner.expects(:joinOrFail).with(@wait_thr).yields InstanceAgent::ThreadJoiner.expects(:new).with(30).returns(@thread_joiner) @wait_thr.stubs(:pid).returns(1234) @@ -307,7 +322,7 @@ def create_full_hook_executor Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr]) @app_spec = {"version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=>[{'location'=>'test', 'timeout'=>"#{timeout}"}]}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor end context "STDOUT left open" do @@ -341,7 +356,7 @@ def create_full_hook_executor setup do @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test", "runas"=>"user"}]}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor mock_pipe = mock Open3.stubs(:popen3).with(@child_env, 'su user -c ' + @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr]) end @@ -374,7 +389,7 @@ def create_full_hook_executor setup do @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test"}]}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr]) end @@ -406,7 +421,7 @@ def create_full_hook_executor setup do @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test"}]}} YAML.stubs(:load).returns(@app_spec) - @hook_executor = create_full_hook_executor + @hook_executor = create_hook_executor Open3.stubs(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr]) InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(false) end