cfhighlander 0.8.2 → 0.9.0.alpha.1556659966

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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