Skip to content

Commit 6a96bb5

Browse files
author
nyo
committed
Allow overriding appspec path + fallback to appspec.yml and appspec.yaml
Removed leftover debug typos Renamed variable to not have default in the name Added arg --appspec-path Allow for appspec-filename to be overridden
1 parent a0e45b6 commit 6a96bb5

File tree

6 files changed

+229
-41
lines changed

6 files changed

+229
-41
lines changed

lib/aws/codedeploy/local/deployer.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def execute_events(args)
7676
deployment_group_id = args['--deployment-group']
7777
events = self.class.events_from_comma_separated_list(args['--events'])
7878

79-
spec = build_spec(args['--bundle-location'], args['--type'], deployment_group_id, args['--file-exists-behavior'], all_possible_lifecycle_events(events), args['--deployment-group-name'], args['--application-name'])
79+
spec = build_spec(args['--bundle-location'], args['--type'], deployment_group_id, args['--file-exists-behavior'], all_possible_lifecycle_events(events), args['--deployment-group-name'], args['--application-name'], args['--appspec-filename'])
8080

8181
command_executor = InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor.new(:hook_mapping => hook_mapping(events))
8282
all_lifecycle_events_to_execute = add_download_bundle_and_install_events(ordered_lifecycle_events(events))
@@ -126,14 +126,15 @@ def hook_mapping(events)
126126
Hash[all_events_plus_default_events_minus_required_events.map{|h|[h,[h]]}]
127127
end
128128

129-
def build_spec(location, bundle_type, deployment_group_id, file_exists_behavior, all_possible_lifecycle_events, deployment_group_name, application_name)
129+
def build_spec(location, bundle_type, deployment_group_id, file_exists_behavior, all_possible_lifecycle_events, deployment_group_name, application_name, appspec_filename)
130130
@deployment_id = self.class.random_deployment_id
131131
puts "Starting to execute deployment from within folder #{deployment_folder(deployment_group_id, @deployment_id)}"
132132
OpenStruct.new({
133133
:format => "TEXT/JSON",
134134
:payload => {
135135
"ApplicationId" => location,
136136
"ApplicationName" => application_name || location,
137+
"AppSpecFilename" => appspec_filename || "appspec.yml",
137138
"DeploymentGroupId" => deployment_group_id,
138139
"DeploymentGroupName" => deployment_group_name || "LocalFleet",
139140
"DeploymentId" => @deployment_id,

lib/instance_agent/plugins/codedeploy/command_executor.rb

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def map
154154
:deployment_root_dir => deployment_root_dir(deployment_spec),
155155
:last_successful_deployment_dir => last_successful_deployment_dir(deployment_spec.deployment_group_id),
156156
:most_recent_deployment_dir => most_recent_deployment_dir(deployment_spec.deployment_group_id),
157-
:app_spec_path => app_spec_path)
157+
:app_spec_path => deployment_spec.app_spec_path)
158158
script_log.concat_log(hook_command.execute)
159159
end
160160
script_log.log
@@ -183,7 +183,7 @@ def last_successful_deployment_dir(deployment_group)
183183
return unless File.exist? last_successful_install_file_location
184184
File.open last_successful_install_file_location do |f|
185185
return f.read.chomp
186-
end
186+
end
187187
end
188188

189189
private
@@ -192,29 +192,30 @@ def most_recent_deployment_dir(deployment_group)
192192
return unless File.exist? most_recent_install_file_location
193193
File.open most_recent_install_file_location do |f|
194194
return f.read.chomp
195-
end
195+
end
196196
end
197197

198198
private
199199
def default_app_spec(deployment_spec)
200-
default_app_spec_location = File.join(archive_root_dir(deployment_spec), app_spec_path)
201-
log(:debug, "Checking for app spec in #{default_app_spec_location}")
202-
validate_app_spec_hooks(ApplicationSpecification::ApplicationSpecification.parse(File.read(default_app_spec_location)), deployment_spec.all_possible_lifecycle_events)
200+
app_spec_location = app_spec_real_path(deployment_spec)
201+
validate_app_spec_hooks(app_spec_location, deployment_spec.all_possible_lifecycle_events)
203202
end
204203

205204
private
206-
def validate_app_spec_hooks(app_spec, all_possible_lifecycle_events)
205+
def validate_app_spec_hooks(app_spec_location, all_possible_lifecycle_events)
206+
app_spec = ApplicationSpecification::ApplicationSpecification.parse(File.read(app_spec_location))
207+
app_spec_filename = File.basename(app_spec_location)
207208
unless all_possible_lifecycle_events.nil?
208209
app_spec_hooks_plus_hooks_from_mapping = app_spec.hooks.keys.to_set.merge(@hook_mapping.keys).to_a
209210
unless app_spec_hooks_plus_hooks_from_mapping.to_set.subset?(all_possible_lifecycle_events.to_set)
210211
unknown_lifecycle_events = app_spec_hooks_plus_hooks_from_mapping - all_possible_lifecycle_events
211-
raise ArgumentError.new("appspec.yml file contains unknown lifecycle events: #{unknown_lifecycle_events}")
212+
raise ArgumentError.new("#{app_spec_filename} file contains unknown lifecycle events: #{unknown_lifecycle_events}")
212213
end
213214

214215
app_spec_hooks_plus_hooks_from_default_mapping = app_spec.hooks.keys.to_set.merge(InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller::DEFAULT_HOOK_MAPPING.keys).to_a
215216
custom_hooks_not_found_in_appspec = custom_lifecycle_events(all_possible_lifecycle_events) - app_spec_hooks_plus_hooks_from_default_mapping
216217
unless (custom_hooks_not_found_in_appspec).empty?
217-
raise ArgumentError.new("You specified a lifecycle event which is not a default one and doesn't exist in your appspec.yml file: #{custom_hooks_not_found_in_appspec.join(',')}")
218+
raise ArgumentError.new("You specified a lifecycle event which is not a default one and doesn't exist in your #{app_spec_filename} file: #{custom_hooks_not_found_in_appspec.join(',')}")
218219
end
219220
end
220221

@@ -238,7 +239,7 @@ def most_recent_install_file_path(deployment_group)
238239
private
239240
def download_from_s3(deployment_spec, bucket, key, version, etag)
240241
log(:debug, "Downloading artifact bundle from bucket '#{bucket}' and key '#{key}', version '#{version}', etag '#{etag}'")
241-
242+
242243
s3 = Aws::S3::Client.new(s3_options)
243244

244245
File.open(artifact_bundle(deployment_spec), 'wb') do |file|
@@ -269,14 +270,14 @@ def s3_options
269270
if !InstanceAgent::Config.config[:s3_endpoint_override].to_s.empty?
270271
options[:endpoint] = URI(InstanceAgent::Config.config[:s3_endpoint_override])
271272
elsif InstanceAgent::Config.config[:use_fips_mode]
272-
#S3 Fips pseudo-regions are not supported by the SDK yet
273+
#S3 Fips pseudo-regions are not supported by the SDK yet
273274
#source for the URL: https://aws.amazon.com/compliance/fips/
274275
options[:endpoint] = "https://s3-fips.#{region}.amazonaws.com"
275-
end
276+
end
276277
proxy_uri = nil
277278
if InstanceAgent::Config.config[:proxy_uri]
278279
proxy_uri = URI(InstanceAgent::Config.config[:proxy_uri])
279-
end
280+
end
280281
options[:http_proxy] = proxy_uri
281282

282283
if InstanceAgent::Config.config[:log_aws_wire]
@@ -288,10 +289,10 @@ def s3_options
288289
64 * 1024 * 1024)
289290
options[:http_wire_trace] = true
290291
end
291-
292-
options
293-
end
294-
292+
293+
options
294+
end
295+
295296
private
296297
def download_from_github(deployment_spec, account, repo, commit, anonymous, token)
297298

@@ -402,7 +403,7 @@ def unpack_bundle(cmd, bundle_file, deployment_spec)
402403

403404
# If the top level of the archive is a directory that contains an appspec,
404405
# strip that before giving up
405-
if ((archive_root_files.size == 1) &&
406+
if ((archive_root_files.size == 1) &&
406407
File.directory?(File.join(dst, archive_root_files[0])) &&
407408
Dir.entries(File.join(dst, archive_root_files[0])).grep(/appspec/i).any?)
408409
log(:info, "Stripping leading directory from archive bundle contents.")
@@ -417,7 +418,7 @@ def unpack_bundle(cmd, bundle_file, deployment_spec)
417418
FileUtils.mv(nested_archive_root, dst)
418419
FileUtils.rmdir(tmp_dst)
419420

420-
log(:debug, Dir.entries(dst).join("; "))
421+
log(:debug, Dir.entries(dst).join("; "))
421422
end
422423
end
423424

@@ -433,7 +434,7 @@ def update_most_recent_install(deployment_spec)
433434
File.open(most_recent_install_file_path(deployment_spec.deployment_group_id), 'w+') do |f|
434435
f.write deployment_root_dir(deployment_spec)
435436
end
436-
end
437+
end
437438

438439
private
439440
def cleanup_old_archives(deployment_spec)
@@ -445,7 +446,7 @@ def cleanup_old_archives(deployment_spec)
445446

446447
full_path_deployment_archives = deployment_archives.map{ |f| File.join(ProcessManager::Config.config[:root_dir], deployment_group, f)}
447448
full_path_deployment_archives.delete(deployment_root_dir(deployment_spec))
448-
449+
449450
extra = full_path_deployment_archives.size - @archives_to_retain + 1
450451
return unless extra > 0
451452

@@ -458,7 +459,7 @@ def cleanup_old_archives(deployment_spec)
458459

459460
# Absolute path takes care of relative root directories
460461
directories = oldest_extra.map{ |f| File.absolute_path(f) }
461-
log(:debug,"Delete Files #{directories}" )
462+
log(:debug, "Delete Files #{directories}")
462463
InstanceAgent::Platform.util.delete_dirs_command(directories)
463464

464465
end
@@ -473,6 +474,24 @@ def app_spec_path
473474
'appspec.yml'
474475
end
475476

477+
# Checks for existence the possible extensions of the app_spec_path (.yml and .yaml)
478+
private
479+
def app_spec_real_path(deployment_spec)
480+
app_spec_param_location = File.join(archive_root_dir(deployment_spec), deployment_spec.app_spec_path)
481+
app_spec_yaml_location = File.join(archive_root_dir(deployment_spec), "appspec.yaml")
482+
app_spec_yml_location = File.join(archive_root_dir(deployment_spec), "appspec.yml")
483+
if File.exist? app_spec_param_location
484+
log(:debug, "Using appspec file #{app_spec_param_location}")
485+
app_spec_param_location
486+
elsif File.exist? app_spec_yaml_location
487+
log(:debug, "Using appspec file #{app_spec_yaml_location}")
488+
app_spec_yaml_location
489+
else
490+
log(:debug, "Using appspec file #{app_spec_yml_location}")
491+
app_spec_yml_location
492+
end
493+
end
494+
476495
private
477496
def description
478497
self.class.to_s

lib/instance_agent/plugins/codedeploy/deployment_specification.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class DeploymentSpecification
1313
attr_accessor :external_account, :repository, :commit_id, :anonymous, :external_auth_token
1414
attr_accessor :file_exists_behavior
1515
attr_accessor :local_location, :all_possible_lifecycle_events
16+
attr_accessor :app_spec_path
1617
class << self
1718
attr_accessor :cert_store
1819
end
@@ -47,6 +48,12 @@ def initialize(data)
4748
@deployment_creator = data["DeploymentCreator"] || "user"
4849
@deployment_type = data["DeploymentType"] || "IN_PLACE"
4950

51+
if property_set?(data, "AppSpecFilename")
52+
@app_spec_path = data["AppSpecFilename"]
53+
else
54+
@app_spec_path = "appspec.yml"
55+
end
56+
5057
raise 'Must specify a revison' unless data["Revision"]
5158
@revision_source = data["Revision"]["RevisionType"]
5259
raise 'Must specify a revision source' unless @revision_source
@@ -99,7 +106,7 @@ def initialize(data)
99106
end
100107
# Decrypts the envelope /deployment specs
101108
# Params:
102-
# envelope: deployment specification thats to be cheked and decrypted
109+
# envelope: deployment specification that's to be checked and decrypted
103110
def self.parse(envelope)
104111
raise 'Provided deployment spec was nil' if envelope.nil?
105112

spec/aws/codedeploy/local/deployer_spec.rb

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,24 +562,86 @@ def create_config_file(working_directory, log_dir = nil)
562562
AWS::CodeDeploy::Local::Deployer.new(@config_file_location).execute_events(args)
563563
end
564564
end
565+
566+
context 'when appspec-filename is not in the args' do
567+
let(:args) do
568+
{"deploy"=>true,
569+
'--file-exists-behavior'=>InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification::DEFAULT_FILE_EXISTS_BEHAVIOR,
570+
"--location"=>true,
571+
"--bundle-location"=>SAMPLE_FILE_BUNDLE,
572+
"--type"=>'tgz',
573+
'--deployment-group'=>DEPLOYMENT_GROUP_ID,
574+
'--deployment-group-name'=>SAMPLE_DEPLOYMENT_GROUP_NAME,
575+
"--help"=>false,
576+
"--version"=>false}
577+
end
578+
it 'generates a spec with the provided value' do
579+
allow(File).to receive(:exists?).with(SAMPLE_FILE_BUNDLE).and_return(true)
580+
executor = double(InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor)
581+
582+
expect(InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor).to receive(:new).
583+
with(:hook_mapping => EXPECTED_HOOK_MAPPING).
584+
and_return(executor)
585+
586+
AWS::CodeDeploy::Local::Deployer::DEFAULT_ORDERED_LIFECYCLE_EVENTS.each do |name|
587+
expect(executor).to receive(:execute_command).with(
588+
OpenStruct.new(:command_name => name),
589+
deployment_spec(SAMPLE_FILE_BUNDLE, 'Local File', 'tgz',
590+
AWS::CodeDeploy::Local::Deployer::DEFAULT_ORDERED_LIFECYCLE_EVENTS, deployment_group_name:SAMPLE_DEPLOYMENT_GROUP_NAME, application_name: nil)).once.ordered
591+
end
592+
AWS::CodeDeploy::Local::Deployer.new(@config_file_location).execute_events(args)
593+
end
594+
end
595+
596+
context 'when appspec-filename is in the args' do
597+
let(:args) do
598+
{"deploy"=>true,
599+
'--file-exists-behavior'=>InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification::DEFAULT_FILE_EXISTS_BEHAVIOR,
600+
"--location"=>true,
601+
"--appspec-filename"=>"appspec-override.yaml",
602+
"--bundle-location"=>SAMPLE_FILE_BUNDLE,
603+
"--type"=>'tgz',
604+
'--deployment-group'=>DEPLOYMENT_GROUP_ID,
605+
'--deployment-group-name'=>SAMPLE_DEPLOYMENT_GROUP_NAME,
606+
"--help"=>false,
607+
"--version"=>false}
608+
end
609+
it 'generates a spec with the provided value' do
610+
allow(File).to receive(:exists?).with(SAMPLE_FILE_BUNDLE).and_return(true)
611+
executor = double(InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor)
612+
613+
expect(InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor).to receive(:new).
614+
with(:hook_mapping => EXPECTED_HOOK_MAPPING).
615+
and_return(executor)
616+
617+
AWS::CodeDeploy::Local::Deployer::DEFAULT_ORDERED_LIFECYCLE_EVENTS.each do |name|
618+
expect(executor).to receive(:execute_command).with(
619+
OpenStruct.new(:command_name => name),
620+
deployment_spec(SAMPLE_FILE_BUNDLE, 'Local File', 'tgz',
621+
AWS::CodeDeploy::Local::Deployer::DEFAULT_ORDERED_LIFECYCLE_EVENTS, deployment_group_name:SAMPLE_DEPLOYMENT_GROUP_NAME, application_name: nil, appspec_filename: "appspec-override.yaml")).once.ordered
622+
end
623+
AWS::CodeDeploy::Local::Deployer.new(@config_file_location).execute_events(args)
624+
end
625+
end
626+
565627
end
566628

567-
def deployment_spec(location, revision_type, bundle_type, all_possible_lifecycle_events, s3revision_includes_version=false, s3revision_includes_etag=false, deployment_group_id=DEPLOYMENT_GROUP_ID, file_exists_behavior=InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification::DEFAULT_FILE_EXISTS_BEHAVIOR, deployment_group_name:nil, application_name:nil)
629+
def deployment_spec(location, revision_type, bundle_type, all_possible_lifecycle_events, s3revision_includes_version=false, s3revision_includes_etag=false, deployment_group_id=DEPLOYMENT_GROUP_ID, file_exists_behavior=InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification::DEFAULT_FILE_EXISTS_BEHAVIOR, deployment_group_name:nil, application_name:nil, appspec_filename: nil)
568630
revision_data_key = revision_data(revision_type, location, bundle_type, s3revision_includes_version, s3revision_includes_etag).keys.first
569631
revision_data_value = revision_data(revision_type, location, bundle_type, s3revision_includes_version, s3revision_includes_etag).values.first
570632
OpenStruct.new({
571633
:format => "TEXT/JSON",
572634
:payload => {
573635
"ApplicationId" => location,
574636
"ApplicationName" => application_name || location,
637+
"AppSpecFilename" => appspec_filename || "appspec.yml",
575638
"DeploymentGroupId" => deployment_group_id,
576639
"DeploymentGroupName" => deployment_group_name || "LocalFleet",
577640
"DeploymentId" => TEST_DEPLOYMENT_ID,
578641
"AgentActionOverrides" => {"AgentOverrides" => {"FileExistsBehavior" => file_exists_behavior}},
579642
"Revision" => {"RevisionType" => revision_type,
580643
revision_data_key => revision_data_value},
581644
"AllPossibleLifecycleEvents" => all_possible_lifecycle_events
582-
583645
}.to_json.to_s
584646
})
585647
end

0 commit comments

Comments
 (0)