cfhighlander 0.8.2 → 0.9.0.alpha.1556659966

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02fe854d713ab142174a9116c6d6710f9669eaf0256195b48eb1da86d191e0d8
4
- data.tar.gz: f0aed4b99a952f156b95cd68670e9720173948e7532441dd989738b91fae94de
3
+ metadata.gz: 001df982a303f15386c95edefe223bb6d18e67adc357f0164b08f7b11b6735df
4
+ data.tar.gz: 287a98c600c8697d05b0439bfe44779022a32c48a48504d46d07f8fde61706ef
5
5
  SHA512:
6
- metadata.gz: 4021a5058a31c3bb410d221184dabf8770659fad62047af43525ec599d03fe543d6d583e607839b375da37bfcb654267114dc273d8352c9d8dd321442da2612d
7
- data.tar.gz: b3ca6ee6a1945654fff3155c739f093a075300ccd784e8bc525a8936f3dd768bab9220fb1ffbd07fa4caa8fcec6ea9e07caa1d8eb40841c3588bd41efed79dc7
6
+ metadata.gz: 988727c8d0185cdd2f7b33c790156374e4e01aae50b5424bcb1072ae1f3f1bf53ff5b0c399c8daf4c610babe6fdd7872bd68ed48e93268b91e468b06dc33f0ab
7
+ data.tar.gz: 6f30346bb04ebdb05ddcd93ecdd618a00b959f72d35c5ced2aaddd39b937ccecc0e3462adffe749e04427f300ad3839c3ea9ce7b730a53460952cdd974e18870
data/README.md CHANGED
@@ -653,12 +653,140 @@ so extension methods can be consumed within cfndsl template.
653
653
 
654
654
  ### LambdaFunctions
655
655
 
656
+
657
+ Cfhighlander supports following in terms of lambda source code management
658
+
659
+ - Package and deploy lambda that has source code published to s3 as zip archive
660
+ - Package and deploy lambda that has source code published to http(s) url
661
+ - Package and deploy lambda with absolute source code location, or relative to component
662
+ root directory
663
+ - When extending certain highlander component, of
664
+ - Execute arbitrary 'package command' before creating final archive. This allows for downloading
665
+ code dependencies
666
+
667
+ #### Configuration and rendering
668
+
669
+ Lambda functions are defined in cfhighlander templates using `LambdaFunctions` DSL statement:
670
+
671
+ ```ruby
672
+ CfhighlanderTemplate do
673
+ Name 'my_app'
674
+ LambdaFunctions 'highlanderdocoexample'
675
+ end
676
+ ```
677
+
678
+ In example above, `lambdas` - value passed to `LambdaFunctions` dsl statement is actually
679
+ cfhighlander component configuration key, under which lambda functions, and their respective
680
+ IAM roles are defined. Consider configuration below - all keys are commented with explanation
681
+
682
+ ```yaml
683
+ highlanderdocoexample:
684
+
685
+ # custom policies can be referenced in roles
686
+ custom_policies:
687
+ cognito:
688
+ action:
689
+ - cognito-idp:*
690
+ resource: '*'
691
+
692
+ # at least one role must be defined
693
+ roles:
694
+ default:
695
+ # using one of the default policies, or custom policies defined above
696
+ # defined at https://github.com/theonestack/cfhighlander/blob/develop/cfndsl_ext/config/managed_policies.yaml
697
+ policies_inline:
698
+ - cloudwatch-logs
699
+ - cognito
700
+
701
+ # managed IAM policies are supported as well
702
+ policies_managed:
703
+ - arn:aws:iam::aws:policy/IAMReadOnlyAccess
704
+ - Fn::Sub: 'arn:aws:iam::${AWS::AccountId}:policy/my_app_policy'
705
+
706
+ # you can have multiple functions defined, each as key under 'functions'
707
+ functions:
708
+ myapp:
709
+
710
+ # link to a role key above defined - mandatory
711
+ role: default
712
+
713
+ # code location. Can be file, archive, s3://url or http(s)://url.
714
+ # mandatory configuration option
715
+ code: src/app.py
716
+
717
+ # lambda runtime
718
+ runtime: python3.6
719
+
720
+ # functions that are not named are having their name auto generated via cloudformation
721
+ # this key defaults to false if not given
722
+ named: true
723
+
724
+ # if function is named, either top level key (myapp) will be used, or explicit name
725
+ # this key is optional, and used only if named: is set to true
726
+ function_name:
727
+ Fn::Sub: '${EnvironmentName}-myapp-${EnvironmentVersion}'
728
+
729
+ # function timeout (defaults to 30)
730
+ timeout: 30
731
+
732
+ # lambda function entrypoint
733
+ handler: app.index
734
+
735
+ # command to install any dependencies (optional)
736
+ # if you don't want to get prompted for every command execution use -q (quiet) option
737
+ package_cmd: 'pip3 install -r requirements.txt -t .'
738
+
739
+ # (optional) allowed source. e.g. invocation using SNS
740
+ # for every allowed source, source_arn can be provided optionally
741
+ allowed_sources:
742
+ -
743
+ principal: sns.amazonaws.com
744
+ -
745
+ principal: sns.amazonaws.com
746
+ source_arn: arn:aws:sns:us-east-2:123456789012:my-topic
747
+
748
+ # (optional) invoke function on a schedule, with optional payload
749
+ schedules:
750
+ - cronExpression: 'rate(1 minute)'
751
+ payload: '{ "message": "ping" }'
752
+ ```
753
+
754
+
656
755
  #### Packaging and publishing
657
756
 
658
- #### Rendering
757
+ During cfhighlander compilation process, every defined lambda functio goes through process
758
+ of packaging:
759
+
760
+ - If s3 URI or http(s) uri is given as function code, it is being downloaded
761
+ - Temporary packaging directory is created
762
+ - If `package_cmd` key is given, this command is being executed in temporary directory
763
+ - Whole temporary directory is compressed and moved to `out/lambdas/$function.$timestamp.zip`
764
+ - Sha256 hash is calculated for given file and rendered into cloudformation as function
765
+ version
766
+ - Packaging information is rendered into `out/lambdas/$function.$timestamp.zip.info.yaml`
767
+ and added to final archive
768
+
769
+ Any archive with `*.zip` extension will be uploaded to s3 with `cfpublish` command
770
+
659
771
 
660
772
  #### Referencing
661
773
 
774
+ As all of the lambda functions are rendered as cloudformation resources, they can be
775
+ referenced in other blocks. E.g. with example above, application component could have
776
+ following output defined in component's cfndsl file
777
+
778
+ ```ruby
779
+ # myapp.cfndsl.rb
780
+ CloudFormation do
781
+
782
+ Output('MyAppFunctionName') do
783
+ Value(Ref('myapp'))
784
+ end
785
+
786
+ end
787
+
788
+
789
+ ```
662
790
 
663
791
  ## Finding templates and creating components
664
792
 
data/bin/cfhighlander.rb CHANGED
@@ -39,6 +39,7 @@ class HighlanderCli < Thor
39
39
 
40
40
  # compile cfndsl template
41
41
  component_compiler = Cfhighlander::Compiler::ComponentCompiler.new(component)
42
+ component_compiler.clear_out_dir
42
43
  component_compiler.writeConfig(true)
43
44
  end
44
45
 
@@ -59,6 +60,7 @@ class HighlanderCli < Thor
59
60
 
60
61
  # compile cfndsl template
61
62
  component_compiler = Cfhighlander::Compiler::ComponentCompiler.new(component)
63
+ component_compiler.clear_out_dir
62
64
  component_compiler.silent_mode = options[:quiet]
63
65
  out_format = options[:format]
64
66
  component_compiler.compileCfnDsl out_format
@@ -102,6 +104,7 @@ class HighlanderCli < Thor
102
104
 
103
105
  # compile cloud formation
104
106
  component_compiler = Cfhighlander::Compiler::ComponentCompiler.new(component)
107
+ component_compiler.clear_out_dir
105
108
  component_compiler.silent_mode = options[:quiet]
106
109
  out_format = options[:format]
107
110
  component_compiler.compileCloudFormation out_format
@@ -43,7 +43,11 @@ def render_lambda_functions(cfndsl, lambdas, lambda_metadata, distribution)
43
43
  end
44
44
 
45
45
  if !lambda_config['named'].nil? && lambda_config['named']
46
- FunctionName(name)
46
+ if lambda_config['function_name'].nil?
47
+ FunctionName(name)
48
+ else
49
+ FunctionName(lambda_config['function_name'])
50
+ end
47
51
  end
48
52
  end
49
53
 
@@ -53,6 +57,33 @@ def render_lambda_functions(cfndsl, lambdas, lambda_metadata, distribution)
53
57
  CodeSha256(lambda_metadata['sha256'][key])
54
58
  end
55
59
 
60
+ # Scheduled triggering of lambda function
61
+ if lambda_config.key?('schedules')
62
+ lambda_config['schedules'].each_with_index do |schedule, index|
63
+ Events_Rule("Lambda#{name}Schedule#{index}") do
64
+ if schedule['cronExpression'].include?('rate')
65
+ then
66
+ expression = schedule['cronExpression']
67
+ else
68
+ expression = "cron(#{schedule['cronExpression']})"
69
+ end
70
+ ScheduleExpression(expression)
71
+ State('ENABLED')
72
+ target = {
73
+ 'Arn' => FnGetAtt(name, 'Arn'), 'Id' => "lambda#{name}",
74
+ }
75
+ target['Input'] = schedule['payload'] if schedule.key?('payload')
76
+ Targets([target])
77
+ end
78
+ Lambda_Permission("Lambda#{name}Schedule#{index}Permission") do
79
+ FunctionName(Ref(name))
80
+ Action('lambda:InvokeFunction')
81
+ Principal('events.amazonaws.com')
82
+ SourceArn FnGetAtt("Lambda#{name}Schedule#{index}", 'Arn')
83
+ end
84
+ end
85
+ end
86
+
56
87
  # Generate lambda function Policy
57
88
  unless lambda_config['allowed_sources'].nil?
58
89
  i = 1
@@ -69,21 +100,6 @@ def render_lambda_functions(cfndsl, lambdas, lambda_metadata, distribution)
69
100
  end
70
101
  end
71
102
 
72
- # Scheduled triggering of lambda function
73
- if lambda_config.key?('schedules')
74
- lambda_config['schedules'].each_with_index do |schedule, index|
75
- Events_Rule("Lambda#{name}Schedule#{index}") do
76
- ScheduleExpression("cron(#{schedule['cronExpression']})")
77
- State('ENABLED')
78
- target = {
79
- 'Arn' => FnGetAtt(name, 'Arn'), 'Id' => "lambda#{name}",
80
- }
81
- target['Input'] = schedule['payload'] if schedule.key?('payload')
82
- Targets([target])
83
- end
84
- end
85
- end
86
-
87
103
 
88
104
  end
89
105
  end
@@ -51,7 +51,7 @@ module Cfhighlander
51
51
 
52
52
  if @@global_extensions_paths.empty?
53
53
  global_extensions_folder = "#{File.dirname(__FILE__)}/../cfndsl_ext"
54
- Dir["#{global_extensions_folder}/*.rb"].each {|f| @@global_extensions_paths << f}
54
+ Dir["#{global_extensions_folder}/*.rb"].each { |f| @@global_extensions_paths << f }
55
55
  end
56
56
 
57
57
  @component.highlander_dsl.subcomponents.each do |sub_component|
@@ -61,14 +61,19 @@ module Cfhighlander
61
61
  end
62
62
  end
63
63
 
64
+ def clear_out_dir
65
+ # Clear previous packages
66
+ FileUtils.rmtree "#{@workdir}/out/"
67
+ end
68
+
64
69
  def process_lambdas=(value)
65
70
  @process_lambdas = value
66
- @sub_components.each {|scc| scc.process_lambdas = value}
71
+ @sub_components.each { |scc| scc.process_lambdas = value }
67
72
  end
68
73
 
69
74
  def silent_mode=(value)
70
75
  @silent_mode = value
71
- @sub_components.each {|scc| scc.silent_mode = value}
76
+ @sub_components.each { |scc| scc.silent_mode = value }
72
77
  end
73
78
 
74
79
  def compileCfnDsl(out_format)
@@ -77,7 +82,7 @@ module Cfhighlander
77
82
  dsl = @component.highlander_dsl
78
83
  component_cfndsl = @component.cfndsl_content
79
84
 
80
- @component.highlander_dsl.subcomponents.each {|sc|
85
+ @component.highlander_dsl.subcomponents.each { |sc|
81
86
  sc.distribution_format = out_format
82
87
  }
83
88
 
@@ -91,7 +96,7 @@ module Cfhighlander
91
96
  'component_cfndsl' => component_cfndsl,
92
97
  'component_requires' => (@@global_extensions_paths + @component.cfndsl_ext_files),
93
98
  'distribution_format' => out_format
94
- }).instance_eval {binding})
99
+ }).instance_eval { binding })
95
100
 
96
101
  # write to output file
97
102
  output_dir = "#{@workdir}/out/cfndsl"
@@ -102,7 +107,7 @@ module Cfhighlander
102
107
  puts "cfndsl template for #{dsl.name} written to #{output_path}"
103
108
  @cfndsl_compiled_path = output_path
104
109
 
105
- @sub_components.each {|subcomponent_compiler|
110
+ @sub_components.each { |subcomponent_compiler|
106
111
  puts "Rendering sub-component cfndsl: #{subcomponent_compiler.component_name}"
107
112
  subcomponent_compiler.compileCfnDsl out_format
108
113
  }
@@ -230,9 +235,6 @@ module Cfhighlander
230
235
 
231
236
  def generateSourceArchives
232
237
 
233
- # Clear previous packages
234
- FileUtils.rmtree "#{@workdir}/output/lambdas"
235
-
236
238
  archive_paths = []
237
239
 
238
240
  # Cached downloads map
@@ -299,13 +301,27 @@ module Cfhighlander
299
301
  # zip local code
300
302
  component = @component
301
303
  component_dir = component.template.template_location
302
- full_path = "#{component_dir}/lambdas/#{lambda_config['code']}"
303
-
304
+ full_path_candidate_1 = "#{component_dir}/lambdas/#{lambda_config['code']}"
305
+ full_path_candidate_2 = "#{component_dir}/#{lambda_config['code']}"
306
+ full_path_candidate_3 = lambda_config['code']
307
+ full_path = full_path_candidate_1
308
+ full_path = full_path_candidate_2 if (File.exist? full_path_candidate_2 and (not File.exist? full_path))
309
+ full_path = full_path_candidate_3 if (File.exist? full_path_candidate_3 and (not File.exist? full_path))
310
+
311
+ # if component extends another component, both parent and child paths should be taken in
312
+ # consideration
304
313
  until (File.exist? full_path or component_dir.nil?)
305
314
  parent_exists = (not component.extended_component.nil?)
306
315
  component = component.extended_component if parent_exists
307
316
  component_dir = component.template.template_location if parent_exists
308
- full_path = "#{component_dir}/lambdas/#{lambda_config['code']}" if parent_exists
317
+ full_path_candidate_1 = "#{component_dir}/lambdas/#{lambda_config['code']}" if parent_exists
318
+ full_path_candidate_2 = "#{component_dir}/#{lambda_config['code']}" if parent_exists
319
+ full_path_candidate_3 = "#{lambda_config['code']}" if parent_exists
320
+
321
+ full_path = full_path_candidate_1
322
+ full_path = full_path_candidate_2 if (File.exist? full_path_candidate_2 and (not File.exist? full_path))
323
+ full_path = full_path_candidate_3 if (File.exist? full_path_candidate_3 and (not File.exist? full_path))
324
+
309
325
  component_dir = nil unless parent_exists
310
326
  end
311
327
  if component_dir.nil?
@@ -343,9 +359,14 @@ module Cfhighlander
343
359
  end
344
360
  end
345
361
  File.delete full_destination_path if File.exist? full_destination_path
346
- zip_generator = Cfhighlander::Util::ZipFileGenerator.new(lambda_source_dir, full_destination_path)
347
- zip_generator.write
348
362
 
363
+ # if source is already zip file, just add manifest to it
364
+ if full_path.end_with? '.zip'
365
+ FileUtils.copy full_path, full_destination_path
366
+ else
367
+ zip_generator = Cfhighlander::Util::ZipFileGenerator.new(lambda_source_dir, full_destination_path)
368
+ zip_generator.write
369
+ end
349
370
  end
350
371
  end
351
372
  # add version information to avoid same package ever deployed 2 times
@@ -357,13 +378,19 @@ module Cfhighlander
357
378
  puts "INFO | Lambda #{name} | Created zip package #{full_destination_path} with digest #{sha256}"
358
379
  @metadata['sha256'][name] = sha256
359
380
  @metadata['version'][name] = timestamp
360
- end
381
+ end if ((not @lambda_config.nil?) and @lambda_config.key? 'functions')
361
382
 
362
383
  return archive_paths
363
384
  end
364
385
 
365
386
  def mergeComponentConfig
366
- @component.config['lambda_metadata'] = @metadata
387
+ if @component.config.key? 'lambda_metadata'
388
+ @metadata.each do |mk,mh|
389
+ mh.each do |k,v| @component.config['lambda_metadata'][mk][k] = v end
390
+ end
391
+ else
392
+ @component.config['lambda_metadata'] = @metadata
393
+ end
367
394
  end
368
395
 
369
396
  end
@@ -1,3 +1,3 @@
1
1
  module Cfhighlander
2
- VERSION="0.8.2".freeze
2
+ VERSION="0.9.0".freeze
3
3
  end
data/lib/util/zip.util.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'zip'
2
2
 
3
-
4
3
  module Cfhighlander
5
4
  module Util
6
5
  ###
@@ -18,7 +17,7 @@ module Cfhighlander
18
17
  def write
19
18
  entries = Dir.entries(@input_dir) - %w(. ..)
20
19
  puts "DEBUG: Compressing #{@input_dir} to #{@output_file}"
21
- ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
20
+ Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
22
21
  write_entries entries, '', zipfile
23
22
  end
24
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfhighlander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0.alpha.1556659966
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikola Tosic
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-02-27 00:00:00.000000000 Z
13
+ date: 2019-04-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: highline
@@ -297,11 +297,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
297
  version: '0'
298
298
  required_rubygems_version: !ruby/object:Gem::Requirement
299
299
  requirements:
300
- - - ">="
300
+ - - ">"
301
301
  - !ruby/object:Gem::Version
302
- version: '0'
302
+ version: 1.3.1
303
303
  requirements: []
304
- rubygems_version: 3.0.2
304
+ rubygems_version: 3.0.3
305
305
  signing_key:
306
306
  specification_version: 4
307
307
  summary: DSL on top of cfndsl. Manage libraries of cloudformation components