require 'test_helper' require 'ostruct' require 'certificate_helper' class DeploymentSpecificationTest < InstanceAgentTestCase 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 context 'The Deployment Specification' do setup do @cert_helper = CertificateHelper.new @deployment_id = SecureRandom.uuid.to_s @deployment_group_id = SecureRandom.uuid.to_s @deployment_group_name = "TestDeploymentGroup" @deployment_creator = "User" @deployment_type = "IN_PLACE" @application_name = "TestApplication" @file_exists_behavior = "RETAIN" @agent_actions_overrides_map = {"FileExistsBehavior" => @file_exists_behavior} @agent_actions_overrides = {"AgentOverrides" => @agent_actions_overrides_map} @bundle_type = 'tar' InstanceAgent::Config.init end context 'With Github Revision' do setup do @githubRevision = { 'Account' => 'owner', 'Repository' => 'repository', 'CommitId' => 'commitid', "BundleType" => @bundle_type } @revision = { "RevisionType" => "GitHub", "GitHubRevision" => @githubRevision } @deployment_spec = { "ApplicationName" => @application_name, "DeploymentId" => @deployment_id, "DeploymentGroupName" => @deployment_group_name, "DeploymentGroupId" => @deployment_group_id, "DeploymentCreator" => @deployment_creator, "DeploymentType" => @deployment_type, "AgentActionOverrides" => @agent_actions_overrides, "Revision" => @revision } @packed_message = generate_signed_message_for(@deployment_spec) end should "populate the deployment with github revision details" do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal @githubRevision, parsed_deployment_spec.revision assert_equal @deployment_id, parsed_deployment_spec.deployment_id assert_equal @deployment_group_name, parsed_deployment_spec.deployment_group_name assert_equal @application_name, parsed_deployment_spec.application_name assert_equal @deployment_creator, parsed_deployment_spec.deployment_creator assert_equal @deployment_type, parsed_deployment_spec.deployment_type assert_equal @bundle_type, parsed_deployment_spec.bundle_type end end context 'With S3 Revision' do setup do @s3Revision = { "Bucket" => "mybucket", "Key" => "mykey", "BundleType" => @bundle_type } @revision = { "RevisionType" => "S3", "S3Revision" => @s3Revision } @deployment_spec = { "ApplicationName" => @application_name, "DeploymentId" => @deployment_id, "DeploymentGroupName" => @deployment_group_name, "DeploymentGroupId" => @deployment_group_id, "DeploymentCreator" => @deployment_creator, "DeploymentType" => @deployment_type, "AgentActionOverrides" => @agent_actions_overrides, "Revision" => @revision } @packed_message = generate_signed_message_for(@deployment_spec) end context "with JSON format" do should "populate the deployment id" do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal @deployment_id, parsed_deployment_spec.deployment_id assert_equal @s3Revision, parsed_deployment_spec.revision assert_equal @deployment_group_name, parsed_deployment_spec.deployment_group_name assert_equal @application_name, parsed_deployment_spec.application_name assert_equal @deployment_creator, parsed_deployment_spec.deployment_creator assert_equal @deployment_type, parsed_deployment_spec.deployment_type assert_equal @bundle_type, parsed_deployment_spec.bundle_type end should "populate the all_possible_lifecycle_events field if present" do deployment_spec_with_all_possible_lifecycle_events = @deployment_spec.clone all_possible_lifecycle_events = ['ExampleLifecycleEvent', 'SecondLifecycleEvent'] deployment_spec_with_all_possible_lifecycle_events['AllPossibleLifecycleEvents'] = all_possible_lifecycle_events parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(generate_signed_message_for(deployment_spec_with_all_possible_lifecycle_events)) assert_equal all_possible_lifecycle_events, parsed_deployment_spec.all_possible_lifecycle_events end end context "with arn deployment id" do setup do @deployment_spec["DeploymentId"] = "arn:aws:codedeploy:region:account:deployment/#{@deployment_id}" @packed_message = generate_signed_message_for(@deployment_spec) end should "populate the Bundle" do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal @deployment_id, parsed_deployment_spec.deployment_id assert_equal @deployment_group_id, parsed_deployment_spec.deployment_group_id assert_equal @s3Revision, parsed_deployment_spec.revision assert_equal @deployment_group_name, parsed_deployment_spec.deployment_group_name assert_equal @application_name, parsed_deployment_spec.application_name assert_equal @deployment_creator, parsed_deployment_spec.deployment_creator assert_equal @deployment_type, parsed_deployment_spec.deployment_type end end context "with an unsupported format" do setup do @packed_message.format = "XML" end should "raise an exception" do assert_raised_with_message("Unsupported DeploymentSpecification format: XML") do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with additional data" do setup do @deployment_spec["AdditionalData"] = "test" @packed_message = generate_signed_message_for(@deployment_spec) end should "populate the Bundle" do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal @deployment_id, parsed_deployment_spec.deployment_id assert_equal @deployment_group_id, parsed_deployment_spec.deployment_group_id assert_equal @s3Revision, parsed_deployment_spec.revision assert_equal @deployment_group_name, parsed_deployment_spec.deployment_group_name assert_equal @application_name, parsed_deployment_spec.application_name assert_equal @deployment_creator, parsed_deployment_spec.deployment_creator assert_equal @deployment_type, parsed_deployment_spec.deployment_type end end context "with a nil format" do setup do @packed_message.format = nil end should "raise an exception" do assert_raised_with_message("Unsupported DeploymentSpecification format: ") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "is nil" do setup do @packed_message = nil end should "raise a runtime exception" do assert_raised_with_message("Provided deployment spec was nil") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with no deployment id" do setup do @deployment_spec.delete("DeploymentId") @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentId") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with null deployment id" do setup do @deployment_spec["DeploymentId"] = nil @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentId") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with empty deployment id" do setup do @deployment_spec["DeploymentId"] = "" @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentId") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with no instance group id" do setup do @deployment_spec.delete("DeploymentGroupId") @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentGroupId") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with null instance group id" do setup do @deployment_spec["DeploymentGroupId"] = nil @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentGroupId") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with empty instance group id" do setup do @deployment_spec["DeploymentGroupId"] = "" @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentGroupId") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with no deployment group name" do setup do @deployment_spec.delete("DeploymentGroupName") @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentGroupName") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with null deployment group name" do setup do @deployment_spec["DeploymentGroupName"] = nil @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentGroupName") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with empty deployment group name" do setup do @deployment_spec["DeploymentGroupName"] = "" @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no DeploymentGroupName") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with no application name" do setup do @deployment_spec.delete("ApplicationName") @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no ApplicationName") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with null application name" do setup do @deployment_spec["ApplicationName"] = nil @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no ApplicationName") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with empty application name" do setup do @deployment_spec["ApplicationName"] = "" @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Deployment Spec has no ApplicationName") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with no target revision" do setup do @deployment_spec.delete("Revision") @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message('Must specify a revison') do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with null target revision" do setup do @deployment_spec["Revision"] = nil @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message('Must specify a revison') do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with empty target revision" do setup do @deployment_spec["Revision"] = "" @packed_message = generate_signed_message_for(@deployment_spec) end should "raise a runtime exception" do assert_raised_with_message("Must specify a revision source") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end end context "with S3 Revision" do should "parse correctly" do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal @s3Revision, parsed_deployment_spec.revision end should "raise when Bucket is missing" do @s3Revision = { "Key" => "mykey", "BundleType" => "tar" } @revision = { "RevisionType" => "S3", "S3Revision" => @s3Revision } @deployment_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_message = generate_signed_message_for(@deployment_spec) assert_raised_with_message("S3Revision in Deployment Spec must specify Bucket, Key and BundleType") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end should "raise when Key is missing" do @s3Revision = { "Bucket" => "mybucket", "BundleType" => @bundle_type } @revision = { "RevisionType" => "S3", "S3Revision" => @s3Revision } @deployment_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_message = generate_signed_message_for(@deployment_spec) assert_raised_with_message("S3Revision in Deployment Spec must specify Bucket, Key and BundleType") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end should "raise when BundleType is missing" do @s3Revision = { "Bucket" => "mybucket", "Key" => "mykey" } @revision = { "RevisionType" => "S3", "S3Revision" => @s3Revision } @deployment_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_message = generate_signed_message_for(@deployment_spec) assert_raised_with_message("S3Revision in Deployment Spec must specify Bucket, Key and BundleType") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end should "raise when bundle type is not a supported format" do @s3Revision = { "Bucket" => "mybucket", "Key" => "mykey", "BundleType" => "bar" } @revision = { "RevisionType" => "S3", "S3Revision" => @s3Revision } @deployment_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_message = generate_signed_message_for(@deployment_spec) assert_raised_with_message("BundleType in S3Revision must be tar, tgz or zip") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) end end should "raise when JSON submitted as PKCS7/JSON" do @packed_message.payload = @deployment_spec.to_json assert_raised_with_message("Could not parse the PKCS7: nested asn1 error") do begin InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) rescue ArgumentError => e raise e.message end end end end context "with Local Revision" do setup do @local_file_revision = { "Location" => "/local/file.tgz", "BundleType" => "zip" } @local_revision = { "RevisionType" => "Local File", "LocalRevision" => @local_file_revision } @deployment_local_revision_spec = { "ApplicationName" => @application_name, "DeploymentId" => @deployment_id, "DeploymentGroupName" => @deployment_group_name, "DeploymentGroupId" => @deployment_group_id, "DeploymentCreator" => @deployment_creator, "DeploymentType" => @deployment_type, "AgentActionOverrides" => @agent_actions_overrides, "Revision" => @local_revision } @packed_local_revision_message = generate_signed_message_for(@deployment_local_revision_spec) InstanceAgent::Config.init end should "parse correctly" do @packed_local_revision_message = generate_signed_message_for(@deployment_local_revision_spec) parsed_deployment_local_revision_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_local_revision_message) assert_equal @local_file_revision, parsed_deployment_local_revision_spec.revision end should "raise when Location is missing" do @local_file_revision = { "BundleType" => "tgz" } @local_revision = { "RevisionType" => "Local File", "LocalRevision" => @local_file_revision } @deployment_local_revision_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @local_revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_local_revision_message = generate_signed_message_for(@deployment_local_revision_spec) assert_raised_with_message("LocalRevision in Deployment Spec must specify Location and BundleType") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_local_revision_message) end end should "raise when BundleType is missing" do @local_file_revision = { "Location" => "/local/file.tgz", } @local_revision = { "RevisionType" => "Local File", "LocalRevision" => @local_file_revision } @deployment_local_revision_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @local_revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_local_revision_message = generate_signed_message_for(@deployment_local_revision_spec) assert_raised_with_message("LocalRevision in Deployment Spec must specify Location and BundleType") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_local_revision_message) end end should "raise when bundle type is not a supported format" do @local_file_revision = { "Location" => "/local/file.tgz", "BundleType" => "bar" } @local_revision = { "RevisionType" => "Local File", "LocalRevision" => @local_file_revision } @deployment_local_revision_spec = { "DeploymentId" => @deployment_id, "DeploymentGroupId" => @deployment_group_id, "Revision" => @local_revision, "DeploymentGroupName" => @deployment_group_name, "ApplicationName" => @application_name } @packed_local_revision_message = generate_signed_message_for(@deployment_local_revision_spec) assert_raised_with_message("BundleType in LocalRevision must be tar, tgz, zip, or directory") do InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_local_revision_message) end end should "raise when JSON submitted as PKCS7/JSON" do @packed_local_revision_message.payload = @deployment_local_revision_spec.to_json assert_raised_with_message("Could not parse the PKCS7: nested asn1 error") do begin InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_local_revision_message) rescue ArgumentError => e raise e.message end end end end context "with app_spec_path" do should "use the variable set by the AppSpecFilename variable" do @deployment_spec["AppSpecFilename"] = "foo.yaml" @packed_message = generate_signed_message_for(@deployment_spec) parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal "foo.yaml", parsed_deployment_spec.app_spec_path end should "fallback to appspec.yml if AppSpecFilename is not set" do @deployment_spec.delete("AppSpecFilename") @packed_message = generate_signed_message_for(@deployment_spec) parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal "appspec.yml", parsed_deployment_spec.app_spec_path end end context "with file_exists_behavior" do should "set file_exists_behavior to DISALLOW when AgentActionOverrides is not set" do @deployment_spec.delete("AgentActionOverrides") @packed_message = generate_signed_message_for(@deployment_spec) parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal "DISALLOW", parsed_deployment_spec.file_exists_behavior end should "set file_exists_behavior to DISALLOW when AgentActionOverrides[\"AgentOverrides\"] is not set" do @deployment_spec["AgentActionOverrides"].delete("AgentOverrides") @packed_message = generate_signed_message_for(@deployment_spec) parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal "DISALLOW", parsed_deployment_spec.file_exists_behavior end should "set file_exists_behavior to DISALLOW when AgentActionOverrides[\"AgentOverrides\"][\"FileExistsBehavior\"] is not set" do @deployment_spec["AgentActionOverrides"]["AgentOverrides"].delete("FileExistsBehavior") @packed_message = generate_signed_message_for(@deployment_spec) parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal "DISALLOW", parsed_deployment_spec.file_exists_behavior end should "set file_exists_behavior when AgentActionOverrides[\"AgentOverrides\"][\"FileExistsBehavior\"] is set" do parsed_deployment_spec = InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.parse(@packed_message) assert_equal @file_exists_behavior, parsed_deployment_spec.file_exists_behavior end end end end end