cloud-mu 3.2.0 → 3.5.0

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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  4. data/bin/mu-adopt +12 -1
  5. data/bin/mu-aws-setup +41 -7
  6. data/bin/mu-azure-setup +34 -0
  7. data/bin/mu-configure +214 -119
  8. data/bin/mu-gcp-setup +37 -2
  9. data/bin/mu-load-config.rb +2 -1
  10. data/bin/mu-node-manage +3 -0
  11. data/bin/mu-refresh-ssl +67 -0
  12. data/bin/mu-run-tests +28 -6
  13. data/bin/mu-self-update +30 -10
  14. data/bin/mu-upload-chef-artifacts +30 -26
  15. data/cloud-mu.gemspec +10 -8
  16. data/cookbooks/mu-master/attributes/default.rb +5 -1
  17. data/cookbooks/mu-master/metadata.rb +2 -2
  18. data/cookbooks/mu-master/recipes/default.rb +81 -26
  19. data/cookbooks/mu-master/recipes/init.rb +197 -62
  20. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  21. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  22. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  23. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  24. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  25. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  26. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  27. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  28. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  30. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  31. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  32. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  33. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  34. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  35. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  36. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  37. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  38. data/extras/Gemfile.lock.bootstrap +394 -0
  39. data/extras/bucketstubs/error.html +0 -0
  40. data/extras/bucketstubs/index.html +0 -0
  41. data/extras/clean-stock-amis +11 -3
  42. data/extras/generate-stock-images +6 -3
  43. data/extras/git_rpm/build.sh +20 -0
  44. data/extras/git_rpm/mugit.spec +53 -0
  45. data/extras/image-generators/AWS/centos7.yaml +19 -16
  46. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  47. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  48. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  49. data/extras/openssl_rpm/build.sh +19 -0
  50. data/extras/openssl_rpm/mussl.spec +46 -0
  51. data/extras/python_rpm/muthon.spec +14 -4
  52. data/extras/ruby_rpm/muby.spec +9 -5
  53. data/extras/sqlite_rpm/build.sh +19 -0
  54. data/extras/sqlite_rpm/muqlite.spec +47 -0
  55. data/install/installer +7 -5
  56. data/modules/mommacat.ru +2 -2
  57. data/modules/mu.rb +14 -7
  58. data/modules/mu/adoption.rb +5 -5
  59. data/modules/mu/cleanup.rb +47 -25
  60. data/modules/mu/cloud.rb +29 -1
  61. data/modules/mu/cloud/dnszone.rb +0 -2
  62. data/modules/mu/cloud/machine_images.rb +1 -1
  63. data/modules/mu/cloud/providers.rb +6 -1
  64. data/modules/mu/cloud/resource_base.rb +16 -7
  65. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  66. data/modules/mu/cloud/wrappers.rb +20 -7
  67. data/modules/mu/config.rb +28 -12
  68. data/modules/mu/config/bucket.rb +31 -2
  69. data/modules/mu/config/cache_cluster.rb +1 -1
  70. data/modules/mu/config/cdn.rb +100 -0
  71. data/modules/mu/config/container_cluster.rb +1 -1
  72. data/modules/mu/config/database.rb +3 -3
  73. data/modules/mu/config/dnszone.rb +4 -3
  74. data/modules/mu/config/endpoint.rb +1 -0
  75. data/modules/mu/config/firewall_rule.rb +1 -1
  76. data/modules/mu/config/function.rb +16 -7
  77. data/modules/mu/config/job.rb +89 -0
  78. data/modules/mu/config/notifier.rb +7 -18
  79. data/modules/mu/config/ref.rb +55 -9
  80. data/modules/mu/config/schema_helpers.rb +12 -3
  81. data/modules/mu/config/server.rb +11 -5
  82. data/modules/mu/config/server_pool.rb +2 -2
  83. data/modules/mu/config/vpc.rb +11 -10
  84. data/modules/mu/defaults/AWS.yaml +106 -106
  85. data/modules/mu/deploy.rb +40 -14
  86. data/modules/mu/groomers/chef.rb +2 -2
  87. data/modules/mu/master.rb +70 -3
  88. data/modules/mu/mommacat.rb +28 -9
  89. data/modules/mu/mommacat/daemon.rb +13 -7
  90. data/modules/mu/mommacat/naming.rb +2 -2
  91. data/modules/mu/mommacat/search.rb +16 -5
  92. data/modules/mu/mommacat/storage.rb +67 -32
  93. data/modules/mu/providers/aws.rb +298 -85
  94. data/modules/mu/providers/aws/alarm.rb +5 -5
  95. data/modules/mu/providers/aws/bucket.rb +284 -50
  96. data/modules/mu/providers/aws/cache_cluster.rb +26 -26
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/providers/aws/collection.rb +16 -16
  99. data/modules/mu/providers/aws/container_cluster.rb +84 -64
  100. data/modules/mu/providers/aws/database.rb +59 -55
  101. data/modules/mu/providers/aws/dnszone.rb +29 -12
  102. data/modules/mu/providers/aws/endpoint.rb +535 -50
  103. data/modules/mu/providers/aws/firewall_rule.rb +32 -26
  104. data/modules/mu/providers/aws/folder.rb +1 -1
  105. data/modules/mu/providers/aws/function.rb +300 -134
  106. data/modules/mu/providers/aws/group.rb +16 -14
  107. data/modules/mu/providers/aws/habitat.rb +4 -4
  108. data/modules/mu/providers/aws/job.rb +469 -0
  109. data/modules/mu/providers/aws/loadbalancer.rb +67 -45
  110. data/modules/mu/providers/aws/log.rb +17 -17
  111. data/modules/mu/providers/aws/msg_queue.rb +22 -13
  112. data/modules/mu/providers/aws/nosqldb.rb +99 -8
  113. data/modules/mu/providers/aws/notifier.rb +137 -65
  114. data/modules/mu/providers/aws/role.rb +119 -83
  115. data/modules/mu/providers/aws/search_domain.rb +166 -30
  116. data/modules/mu/providers/aws/server.rb +209 -118
  117. data/modules/mu/providers/aws/server_pool.rb +95 -130
  118. data/modules/mu/providers/aws/storage_pool.rb +19 -11
  119. data/modules/mu/providers/aws/user.rb +5 -5
  120. data/modules/mu/providers/aws/userdata/linux.erb +5 -4
  121. data/modules/mu/providers/aws/vpc.rb +109 -54
  122. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  123. data/modules/mu/providers/azure.rb +78 -12
  124. data/modules/mu/providers/azure/server.rb +20 -4
  125. data/modules/mu/providers/cloudformation/server.rb +1 -1
  126. data/modules/mu/providers/google.rb +21 -5
  127. data/modules/mu/providers/google/bucket.rb +1 -1
  128. data/modules/mu/providers/google/container_cluster.rb +1 -1
  129. data/modules/mu/providers/google/database.rb +1 -1
  130. data/modules/mu/providers/google/firewall_rule.rb +1 -1
  131. data/modules/mu/providers/google/folder.rb +7 -3
  132. data/modules/mu/providers/google/function.rb +66 -31
  133. data/modules/mu/providers/google/group.rb +1 -1
  134. data/modules/mu/providers/google/habitat.rb +1 -1
  135. data/modules/mu/providers/google/loadbalancer.rb +1 -1
  136. data/modules/mu/providers/google/role.rb +6 -3
  137. data/modules/mu/providers/google/server.rb +1 -1
  138. data/modules/mu/providers/google/server_pool.rb +1 -1
  139. data/modules/mu/providers/google/user.rb +1 -1
  140. data/modules/mu/providers/google/vpc.rb +28 -3
  141. data/modules/tests/aws-jobs-functions.yaml +46 -0
  142. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  143. data/modules/tests/centos6.yaml +4 -0
  144. data/modules/tests/centos7.yaml +4 -0
  145. data/modules/tests/ecs.yaml +2 -2
  146. data/modules/tests/eks.yaml +1 -1
  147. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  148. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  149. data/modules/tests/k8s.yaml +1 -1
  150. data/modules/tests/microservice_app.yaml +288 -0
  151. data/modules/tests/rds.yaml +5 -5
  152. data/modules/tests/regrooms/rds.yaml +5 -5
  153. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  154. data/modules/tests/super_complex_bok.yml +2 -2
  155. data/modules/tests/super_simple_bok.yml +2 -2
  156. metadata +42 -17
@@ -52,14 +52,14 @@ module MU
52
52
  begin
53
53
  MU.log "Creating EC2 Security Group #{groupname}", details: sg_struct
54
54
 
55
- secgroup = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_security_group(sg_struct)
55
+ secgroup = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).create_security_group(sg_struct)
56
56
  @cloud_id = secgroup.group_id
57
57
  rescue Aws::EC2::Errors::InvalidGroupDuplicate
58
58
  MU.log "EC2 Security Group #{groupname} already exists, using it", MU::NOTICE
59
59
  filters = [{name: "group-name", values: [groupname]}]
60
60
  filters << {name: "vpc-id", values: [vpc_id]} if !vpc_id.nil?
61
61
 
62
- secgroup = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_security_groups(filters: filters).security_groups.first
62
+ secgroup = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_security_groups(filters: filters).security_groups.first
63
63
  if secgroup.nil?
64
64
  raise MuError, "Failed to locate security group named #{groupname}, even though EC2 says it already exists", caller
65
65
  end
@@ -67,25 +67,25 @@ module MU
67
67
  end
68
68
 
69
69
  begin
70
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_security_groups(group_ids: [secgroup.group_id])
70
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_security_groups(group_ids: [secgroup.group_id])
71
71
  rescue Aws::EC2::Errors::InvalidGroupNotFound
72
72
  MU.log "#{secgroup.group_id} not yet ready, waiting...", MU::NOTICE
73
73
  sleep 10
74
74
  retry
75
75
  end
76
76
 
77
- MU::Cloud::AWS.createStandardTags(secgroup.group_id, region: @config['region'], credentials: @config['credentials'])
78
- MU::Cloud::AWS.createTag(secgroup.group_id, "Name", groupname, region: @config['region'], credentials: @config['credentials'])
77
+ MU::Cloud::AWS.createStandardTags(secgroup.group_id, region: @region, credentials: @credentials)
78
+ MU::Cloud::AWS.createTag(secgroup.group_id, "Name", groupname, region: @region, credentials: @credentials)
79
79
 
80
80
  if @config['optional_tags']
81
81
  MU::MommaCat.listOptionalTags.each { |key, value|
82
- MU::Cloud::AWS.createTag(secgroup.group_id, key, value, region: @config['region'], credentials: @config['credentials'])
82
+ MU::Cloud::AWS.createTag(secgroup.group_id, key, value, region: @region, credentials: @credentials)
83
83
  }
84
84
  end
85
85
 
86
86
  if @config['tags']
87
87
  @config['tags'].each { |tag|
88
- MU::Cloud::AWS.createTag(secgroup.group_id, tag['key'], tag['value'], region: @config['region'], credentials: @config['credentials'])
88
+ MU::Cloud::AWS.createTag(secgroup.group_id, tag['key'], tag['value'], region: @region, credentials: @credentials)
89
89
  }
90
90
  end
91
91
 
@@ -123,7 +123,7 @@ module MU
123
123
  # Log metadata about this ruleset to the currently running deployment
124
124
  def notify
125
125
  sg_data = MU.structToHash(
126
- MU::Cloud::FirewallRule.find(cloud_id: @cloud_id, region: @config['region'])
126
+ MU::Cloud::FirewallRule.find(cloud_id: @cloud_id, region: @region)
127
127
  )
128
128
  sg_data["group_id"] = @cloud_id
129
129
  sg_data["cloud_id"] = @cloud_id
@@ -151,8 +151,11 @@ module MU
151
151
  rule["firewall_rules"].concat(sgs.map { |s|
152
152
  MU::Config::Ref.get(
153
153
  id: s,
154
+ region: @region,
155
+ credentials: @credentials,
154
156
  cloud: "AWS",
155
- type: "firewall_rule"
157
+ type: "firewall_rule",
158
+ dummy_ok: true
156
159
  )
157
160
  })
158
161
  end
@@ -169,12 +172,12 @@ module MU
169
172
 
170
173
  begin
171
174
  if egress
172
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
175
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).authorize_security_group_egress(
173
176
  group_id: @cloud_id,
174
177
  ip_permissions: ec2_rule
175
178
  )
176
179
  else
177
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
180
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).authorize_security_group_ingress(
178
181
  group_id: @cloud_id,
179
182
  ip_permissions: ec2_rule
180
183
  )
@@ -185,12 +188,12 @@ module MU
185
188
  # existing rules
186
189
  if comment
187
190
  if egress
188
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).update_security_group_rule_descriptions_egress(
191
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).update_security_group_rule_descriptions_egress(
189
192
  group_id: @cloud_id,
190
193
  ip_permissions: ec2_rule
191
194
  )
192
195
  else
193
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).update_security_group_rule_descriptions_ingress(
196
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).update_security_group_rule_descriptions_ingress(
194
197
  group_id: @cloud_id,
195
198
  ip_permissions: ec2_rule
196
199
  )
@@ -202,7 +205,7 @@ module MU
202
205
  # Canonical Amazon Resource Number for this resource
203
206
  # @return [String]
204
207
  def arn
205
- "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":ec2:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":security-group/"+@cloud_id
208
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws")+":ec2:"+@region+":"+MU::Cloud::AWS.credToAcct(@credentials)+":security-group/"+@cloud_id
206
209
  end
207
210
 
208
211
  # Locate an existing security group or groups and return an array containing matching AWS resource descriptors for those that match.
@@ -248,9 +251,9 @@ module MU
248
251
  def toKitten(**_args)
249
252
  bok = {
250
253
  "cloud" => "AWS",
251
- "credentials" => @config['credentials'],
254
+ "credentials" => @credentials,
252
255
  "cloud_id" => @cloud_id,
253
- "region" => @config['region']
256
+ "region" => @region
254
257
  }
255
258
 
256
259
  if !cloud_desc
@@ -381,14 +384,14 @@ module MU
381
384
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
382
385
  # @param region [String]: The cloud provider region
383
386
  # @return [void]
384
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
387
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
385
388
  filters = if flags and flags["vpc_id"]
386
389
  [
387
390
  {name: "vpc-id", values: [flags["vpc_id"]]}
388
391
  ]
389
392
  else
390
393
  filters = [
391
- {name: "tag:MU-ID", values: [MU.deploy_id]}
394
+ {name: "tag:MU-ID", values: [deploy_id]}
392
395
  ]
393
396
  if !ignoremaster
394
397
  filters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
@@ -649,7 +652,7 @@ module MU
649
652
  if rule['firewall_rules']
650
653
  rule['firewall_rules'].each { |sg|
651
654
  if sg['name'] and !sg['deploy_id']
652
- MU::Config.addDependency(acl, sg['name'], "firewall_rule", no_create_wait: true)
655
+ MU::Config.addDependency(acl, sg['name'], "firewall_rule", my_phase: "groom")
653
656
  end
654
657
  }
655
658
  end
@@ -657,7 +660,7 @@ module MU
657
660
  if rule['loadbalancers']
658
661
  rule['loadbalancers'].each { |lb|
659
662
  if lb['name'] and !lb['deploy_id']
660
- MU::Config.addDependency(acl, lb['name'], "loadbalancer", phase: "groom")
663
+ MU::Config.addDependency(acl, lb['name'], "loadbalancer", their_phase: "groom")
661
664
  end
662
665
  }
663
666
  end
@@ -731,7 +734,7 @@ module MU
731
734
  end
732
735
  }
733
736
  MU.log "Removing unconfigured rule in #{@mu_name}", MU::WARN, details: ext_rule
734
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_ingress(
737
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).revoke_security_group_ingress(
735
738
  group_id: @cloud_id,
736
739
  ip_permissions: [ext_rule]
737
740
  )
@@ -797,7 +800,7 @@ module MU
797
800
  if ingress
798
801
  if haverule
799
802
  begin
800
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_ingress(
803
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).revoke_security_group_ingress(
801
804
  group_id: @cloud_id,
802
805
  ip_permissions: [haverule]
803
806
  )
@@ -805,7 +808,7 @@ module MU
805
808
  end
806
809
  end
807
810
  begin
808
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
811
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).authorize_security_group_ingress(
809
812
  group_id: @cloud_id,
810
813
  ip_permissions: [rule]
811
814
  )
@@ -818,14 +821,14 @@ module MU
818
821
  if egress
819
822
  if haverule
820
823
  begin
821
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_egress(
824
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).revoke_security_group_egress(
822
825
  group_id: @cloud_id,
823
826
  ip_permissions: [haverule]
824
827
  )
825
828
  rescue Aws::EC2::Errors::InvalidPermissionNotFound
826
829
  end
827
830
  end
828
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
831
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).authorize_security_group_egress(
829
832
  group_id: @cloud_id,
830
833
  ip_permissions: [rule]
831
834
  )
@@ -860,8 +863,11 @@ module MU
860
863
  p_start = rule['port'].to_i
861
864
  p_end = rule['port'].to_i
862
865
  elsif rule['proto'] != "icmp"
863
- raise MuError, "Can't create a TCP or UDP security group rule without specifying ports: #{rule}"
866
+ MU.log "Can't create a TCP or UDP security group rule without specifying ports, assuming 'all'", MU::WARN, details: rule
867
+ p_start = "0"
868
+ p_end = "65535"
864
869
  end
870
+
865
871
  if rule['proto'] != "icmp"
866
872
  if p_start.nil? or p_end.nil?
867
873
  raise MuError, "Got nil ports out of rule #{rule}"
@@ -59,7 +59,7 @@ module MU
59
59
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
60
60
  # @param region [String]: The cloud provider region
61
61
  # @return [void]
62
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
62
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
63
63
  end
64
64
 
65
65
  # Locate an existing AWS organization. If no identifying parameters are specified, this will return a description of the Organization which owns the account for our credentials.
@@ -18,6 +18,18 @@ module MU
18
18
  # A function as configured in {MU::Config::BasketofKittens::functions}
19
19
  class Function < MU::Cloud::Function
20
20
 
21
+ # If we have sibling resources in our deployment, automatically inject
22
+ # interesting things about them into our function's environment
23
+ # variables.
24
+ SIBLING_VARS = {
25
+ "servers" => ["private_ip_address", "public_ip_address"],
26
+ "search_domains" => ["endpoint"],
27
+ "databases" => ["endpoint"],
28
+ "endpoints" => ["url"],
29
+ "notifiers" => ["TopicArn"],
30
+ "nosqldbs" => ["table_arn"]
31
+ }
32
+
21
33
  # Initialize this cloud resource object. Calling +super+ will invoke the initializer defined under {MU::Cloud}, which should set the attribtues listed in {MU::Cloud::PUBLIC_ATTRS} as well as applicable dependency shortcuts, like +@vpc+, for us.
22
34
  # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
23
35
  def initialize(**args)
@@ -26,10 +38,10 @@ module MU
26
38
  end
27
39
 
28
40
  # Tag this Lambda function
29
- def assign_tag(resource_arn, tag_list, region=@config['region'])
41
+ def assign_tag(resource_arn, tag_list, region=@region)
30
42
  begin
31
43
  tag_list.each do |each_pair|
32
- MU::Cloud::AWS.lambda(region: region, credentials: @config['credentials']).tag_resource({
44
+ MU::Cloud::AWS.lambda(region: region, credentials: @credentials).tag_resource({
33
45
  resource: resource_arn,
34
46
  tags: each_pair
35
47
  })
@@ -42,92 +54,47 @@ module MU
42
54
 
43
55
  # Called automatically by {MU::Deploy#createResources}
44
56
  def create
45
- role_arn = get_role_arn(@config['iam_role'])
46
-
47
- lambda_properties = {
48
- code: {},
49
- function_name: @mu_name,
50
- handler: @config['handler'],
51
- publish: true,
52
- role: role_arn,
53
- runtime: @config['runtime'],
54
- }
55
-
56
- if @config['code']['zip_file']
57
- zip = File.read(@config['code']['zip_file'])
58
- MU.log "Uploading deployment package from #{@config['code']['zip_file']}"
59
- lambda_properties[:code][:zip_file] = zip
60
- else
61
- lambda_properties[:code][:s3_bucket] = @config['code']['s3_bucket']
62
- lambda_properties[:code][:s3_key] = @config['code']['s3_key']
63
- if @config['code']['s3_object_version']
64
- lambda_properties[:code][:s3_object_version] = @config['code']['s3_object_version']
65
- end
66
- end
67
-
68
- if @config.has_key?('timeout')
69
- lambda_properties[:timeout] = @config['timeout'].to_i ## secs
70
- end
71
-
72
- if @config.has_key?('memory')
73
- lambda_properties[:memory_size] = @config['memory'].to_i
74
- end
75
57
 
76
- if @config.has_key?('environment_variables')
77
- lambda_properties[:environment] = {
78
- variables: {@config['environment_variables'][0]['key'] => @config['environment_variables'][0]['value']}
79
- }
80
- end
58
+ lambda_properties = get_properties
81
59
 
82
- lambda_properties[:tags] = {}
83
- MU::MommaCat.listStandardTags.each_pair { |k, v|
84
- lambda_properties[:tags][k] = v
60
+ MU.retrier([Aws::Lambda::Errors::InvalidParameterValueException], max: 5, wait: 10) {
61
+ resp = MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).create_function(lambda_properties)
62
+ @cloud_id = resp.function_name
85
63
  }
86
- if @config['tags']
87
- @config['tags'].each { |tag|
88
- lambda_properties[:tags][tag.key.first] = tag.values.first
89
- }
90
- end
91
64
 
92
- if @config.has_key?('vpc')
93
- sgs = []
94
- if @config['add_firewall_rules']
95
- @config['add_firewall_rules'].each { |sg|
96
- sg = @deploy.findLitterMate(type: "firewall_rule", name: sg['name'])
97
- sgs << sg.cloud_id if sg and sg.cloud_id
98
- }
99
- end
100
- if !@vpc
101
- raise MuError, "Function #{@config['name']} had a VPC configured, but none was loaded"
102
- end
103
- lambda_properties[:vpc_config] = {
104
- :subnet_ids => @vpc.subnets.map { |s| s.cloud_id },
105
- :security_group_ids => sgs
106
- }
107
- end
108
-
109
- retries = 0
110
- resp = begin
111
- MU::Cloud::AWS.lambda(region: @config['region'], credentials: @config['credentials']).create_function(lambda_properties)
112
- rescue Aws::Lambda::Errors::InvalidParameterValueException => e
113
- # Freshly-made IAM roles sometimes aren't really ready
114
- if retries < 5
115
- sleep 10
116
- retries += 1
117
- retry
118
- end
119
- raise e
65
+ # the console does this and docs expect it to be there, so mimic the
66
+ # behavior
67
+ begin
68
+ MU::Cloud::AWS.cloudwatchlogs(region: @region, credentials: @credentials).create_log_group(
69
+ log_group_name: "/aws/lambda/#{@cloud_id}",
70
+ tags: @tags
71
+ )
72
+ rescue Aws::CloudWatchLogs::Errors::ResourceAlreadyExistsException
120
73
  end
121
-
122
- @cloud_id = resp.function_name
123
74
  end
124
75
 
125
76
  # Called automatically by {MU::Deploy#createResources}
126
77
  def groom
127
- desc = MU::Cloud::AWS.lambda(region: @config['region'], credentials: @config['credentials']).get_function(
128
- function_name: @mu_name
129
- )
130
- func_arn = desc.configuration.function_arn if !desc.empty?
78
+ old_props = MU.structToHash(cloud_desc)
79
+
80
+ new_props = get_properties
81
+ code_block = new_props[:code]
82
+ new_props.reject! { |k, _v| [:code, :publish, :tags].include?(k) }
83
+ changes = {}
84
+ new_props.each_pair { |k, v|
85
+ changes[k] = v if v != old_props[k]
86
+ }
87
+ if !changes.empty?
88
+ MU.log "Updating Lambda #{@mu_name}", MU::NOTICE, details: changes
89
+ MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).update_function_configuration(new_props)
90
+ end
91
+
92
+ if @code_sha256 and @code_sha256 != cloud_desc.code_sha_256.chomp
93
+ MU.log "Updating code in Lambda #{@mu_name}", MU::NOTICE, details: { "old" => @code_sha256, "new" => cloud_desc.code_sha_256 }
94
+ code_block[:publish] = true
95
+ code_block[:function_name] = @cloud_id
96
+ MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).update_function_code(code_block)
97
+ end
131
98
 
132
99
  # tag_function = assign_tag(lambda_func.function_arn, @config['tags'])
133
100
 
@@ -141,7 +108,7 @@ module MU
141
108
  ### triggers must exist prior
142
109
  if @config['triggers']
143
110
  @config['triggers'].each { |tr|
144
- trigger_arn = assume_trigger_arns(tr['service'], tr['name'])
111
+ trigger_arn = resolveARN(tr['service'], tr['name'])
145
112
 
146
113
  trigger_properties = {
147
114
  action: "lambda:InvokeFunction",
@@ -151,15 +118,33 @@ module MU
151
118
  statement_id: "#{@mu_name}-ID-1",
152
119
  }
153
120
 
154
- MU.log trigger_properties, MU::DEBUG
121
+ MU.log "Adding #{tr['service']} #{tr['name']} trigger to Lambda function #{@cloud_id}", details: trigger_properties
155
122
  begin
156
- MU::Cloud::AWS.lambda(region: @config['region'], credentials: @config['credentials']).add_permission(trigger_properties)
123
+ MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).add_permission(trigger_properties)
157
124
  rescue Aws::Lambda::Errors::ResourceConflictException
125
+ # just means the permission is already there
158
126
  end
159
- adjust_trigger(tr['service'], trigger_arn, func_arn, @mu_name)
127
+ adjust_trigger(tr['service'], trigger_arn, arn, @mu_name)
160
128
  }
161
129
 
162
130
  end
131
+
132
+ if @config['invoke_on_completion']
133
+ invoke_params = {
134
+ function_name: @cloud_id,
135
+ invocation_type: @config['invoke_on_completion']['invocation_type'],
136
+ log_type: "Tail"
137
+ }
138
+ if @config['invoke_on_completion']['payload']
139
+ invoke_params[:payload] = JSON.generate(@config['invoke_on_completion']['payload'])
140
+ end
141
+ resp = MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).invoke(invoke_params)
142
+ if resp.status_code == 200
143
+ MU.log "Invoked #{@cloud_id}", MU::NOTICE, details: Base64.decode64(resp.log_result)
144
+ else
145
+ MU.log "Invoked #{@cloud_id} and got #{resp.status_code} (#{resp.function_error})", MU::WARN, details: Base64.decode64(resp.log_result)
146
+ end
147
+ end
163
148
  end
164
149
 
165
150
  # Intended to be called by other Mu resources, such as Endpoints (API
@@ -170,16 +155,19 @@ module MU
170
155
  function_name: @mu_name,
171
156
  principal: "#{calling_service}.amazonaws.com",
172
157
  source_arn: calling_arn,
173
- statement_id: "#{calling_service}-#{calling_name}",
158
+ statement_id: "#{calling_service}-#{calling_name.gsub(/[^a-z0-9\-_]/i, '_')}",
174
159
  }
175
160
 
176
161
  begin
177
162
  # XXX There doesn't seem to be an API call to list or view existing
178
163
  # permissions, wtaf. This means we can't intelligently guard this.
179
- MU::Cloud::AWS.lambda(region: @config['region'], credentials: @config['credentials']).add_permission(trigger)
164
+ MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).add_permission(trigger)
165
+ rescue Aws::Lambda::Errors::ValidationException => e
166
+ MU.log e.message+" (calling_arn: #{calling_arn}, calling_service: #{calling_service}, calling_name: #{calling_name})", MU::ERR, details: trigger
167
+ raise e
180
168
  rescue Aws::Lambda::Errors::ResourceConflictException => e
181
169
  if e.message.match(/already exists/)
182
- MU::Cloud::AWS.lambda(region: @config['region'], credentials: @config['credentials']).remove_permission(
170
+ MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).remove_permission(
183
171
  function_name: @mu_name,
184
172
  statement_id: "#{calling_service}-#{calling_name}"
185
173
  )
@@ -192,17 +180,23 @@ module MU
192
180
  end
193
181
 
194
182
  # Look up an ARN for a given trigger type and resource name
195
- def assume_trigger_arns(svc, name)
196
- supported_triggers = %w(apigateway sns events event cloudwatch_event)
183
+ def resolveARN(svc, name)
184
+ supported_triggers = %w(apigateway sns events event cloudwatch_event dynamodb)
197
185
  if supported_triggers.include?(svc.downcase)
198
186
  arn = nil
199
187
  case svc.downcase
200
188
  when 'sns'
201
- arn = "arn:aws:sns:#{@config['region']}:#{MU::Cloud::AWS.credToAcct(@config['credentials'])}:#{name}"
189
+ sib_sns = @deploy.findLitterMate(name: name, type: "notifiers")
190
+ arn = sib_sns ? sib_sns.arn : "arn:aws:sns:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:#{name}"
202
191
  when 'alarm','events', 'event', 'cloudwatch_event'
203
- arn = "arn:aws:events:#{@config['region']}:#{MU::Cloud::AWS.credToAcct(@config['credentials'])}:rule/#{name}"
192
+ sib_event = @deploy.findLitterMate(name: name, type: "job")
193
+ arn = sib_event ? sib_event.arn : "arn:aws:events:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:rule/#{name}"
194
+ when 'dynamodb'
195
+ sib_dynamo = @deploy.findLitterMate(name: name, type: "nosqldb")
196
+ arn = sib_dynamo ? sib_dynamo.arn : "arn:aws:dynamodb:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:table/#{name}"
204
197
  when 'apigateway'
205
- arn = "arn:aws:apigateway:#{@config['region']}:#{MU::Cloud::AWS.credToAcct(@config['credentials'])}:#{name}"
198
+ sib_apig = @deploy.findLitterMate(name: name, type: "endpoints")
199
+ arn = sib_apig ? sib_apig.arn : "arn:aws:apigateway:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:#{name}"
206
200
  when 's3'
207
201
  arn = ''
208
202
  end
@@ -214,21 +208,29 @@ module MU
214
208
  end
215
209
 
216
210
  # XXX placeholder, really; this is going end up being done from Endpoint, Log and Notification resources, I think
217
- def adjust_trigger(trig_type, trig_arn, func_arn, func_id=nil, protocol='lambda',region=@config['region'])
211
+ def adjust_trigger(trig_type, trig_arn, func_arn, func_id=nil, protocol='lambda',region=@region)
218
212
 
219
213
  case trig_type
220
214
 
221
215
  when 'sns'
222
- # XXX don't do this, use MU::Cloud::AWS::Notification
223
- sns_client = MU::Cloud::AWS.sns(region: region, credentials: @config['credentials'])
224
- sns_client.subscribe({
225
- topic_arn: trig_arn,
226
- protocol: protocol,
227
- endpoint: func_arn
228
- })
216
+ MU::Cloud.resourceClass("AWS", "Notifier").subscribe(trig_arn, arn, "lambda", region: @region, credentials: @credentials)
217
+ when 'dynamodb'
218
+ stream = MU::Cloud::AWS.dynamostream(region: @region, credentials: @credentials).list_streams(table_name: trig_arn.sub(/.*?:table\//, '')).streams.first
219
+ # XXX guard this
220
+ MU.log "Adding DynamoDB Stream from #{stream.stream_arn} as trigger for #{@cloud_id}"
221
+ begin
222
+ MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).create_event_source_mapping(
223
+ event_source_arn: stream.stream_arn,
224
+ function_name: @cloud_id,
225
+ starting_position: "TRIM_HORIZON" # ...whatever that is
226
+ )
227
+ rescue ::Aws::Lambda::Errors::ResourceConflictException
228
+ end
229
+
230
+ # MU::Cloud.resourceClass("AWS", "NoSQLDB").subscribe(trig_arn, arn, "lambda", region: @region, credentials: @credentials)
229
231
  when 'event','cloudwatch_event', 'events'
230
232
  # XXX don't do this, use MU::Cloud::AWS::Log
231
- MU::Cloud::AWS.cloudwatch_events(region: region, credentials: @config['credentials']).put_targets({
233
+ MU::Cloud::AWS.cloudwatch_events(region: region, credentials: @credentials).put_targets({
232
234
  rule: @config['trigger']['name'],
233
235
  targets: [
234
236
  {
@@ -237,9 +239,8 @@ module MU
237
239
  }
238
240
  ]
239
241
  })
240
- # when 'apigateway'
241
- # XXX this is actually happening in ::Endpoint... maybe...
242
- # MU.log "Creation of API Gateway integrations not yet implemented, you'll have to do this manually", MU::WARN, details: "(because we'll basically have to implement all of APIG for this)"
242
+ when 'apigateway'
243
+ addTrigger(trig_arn, "lambda", trig_arn.sub(/.*?([a-z0-9\-_]+)$/i, '\1'))
243
244
  end
244
245
  end
245
246
 
@@ -247,9 +248,8 @@ module MU
247
248
  # Return the metadata for this Function rule
248
249
  # @return [Hash]
249
250
  def notify
250
- deploy_struct = MU.structToHash(MU::Cloud::AWS::Function.find(cloud_id: @cloud_id, credentials: @config['credentials'], region: @config['region']).values.first)
251
- deploy_struct['mu_name'] = @mu_name
252
- return deploy_struct
251
+ return nil if !cloud_desc
252
+ MU.structToHash(cloud_desc, stringify_keys: true)
253
253
  end
254
254
 
255
255
  # Does this resource type exist as a global (cloud-wide) artifact, or
@@ -270,14 +270,14 @@ module MU
270
270
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
271
271
  # @param region [String]: The cloud provider region
272
272
  # @return [void]
273
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
273
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
274
274
  MU.log "AWS::Function.cleanup: need to support flags['known']", MU::DEBUG, details: flags
275
275
 
276
276
  MU::Cloud::AWS.lambda(credentials: credentials, region: region).list_functions.functions.each { |f|
277
277
  desc = MU::Cloud::AWS.lambda(credentials: credentials, region: region).get_function(
278
278
  function_name: f.function_name
279
279
  )
280
- if desc.tags and desc.tags["MU-ID"] == MU.deploy_id and (desc.tags["MU-MASTER-IP"] == MU.mu_public_ip or ignoremaster)
280
+ if desc.tags and desc.tags["MU-ID"] == deploy_id and (desc.tags["MU-MASTER-IP"] == MU.mu_public_ip or ignoremaster)
281
281
  MU.log "Deleting Lambda function #{f.function_name}"
282
282
  if !noop
283
283
  MU::Cloud::AWS.lambda(credentials: credentials, region: region).delete_function(
@@ -292,7 +292,7 @@ module MU
292
292
  # Canonical Amazon Resource Number for this resource
293
293
  # @return [String]
294
294
  def arn
295
- cloud_desc.function_arn
295
+ cloud_desc ? cloud_desc.function_arn : nil
296
296
  end
297
297
 
298
298
  # Locate an existing function.
@@ -317,9 +317,9 @@ module MU
317
317
  def toKitten(**_args)
318
318
  bok = {
319
319
  "cloud" => "AWS",
320
- "credentials" => @config['credentials'],
320
+ "credentials" => @credentials,
321
321
  "cloud_id" => @cloud_id,
322
- "region" => @config['region']
322
+ "region" => @region
323
323
  }
324
324
 
325
325
  if !cloud_desc
@@ -333,7 +333,21 @@ module MU
333
333
  bok['runtime'] = cloud_desc.runtime
334
334
  bok['timeout'] = cloud_desc.timeout
335
335
 
336
- function = MU::Cloud::AWS.lambda(region: @config['region'], credentials: @credentials).get_function(function_name: bok['name'])
336
+ function = MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).get_function(function_name: bok['name'])
337
+ # event_srcs = MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).list_event_source_mappings(function_name: @cloud_id)
338
+ # if event_srcs and !event_srcs.event_source_mappings.empty?
339
+ # MU.log "dem mappings tho #{@cloud_id}", MU::WARN, details: event_srcs
340
+ # end
341
+
342
+ # begin
343
+ # invoke_cfg = MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).get_function_event_invoke_config(function_name: @cloud_id)
344
+ # MU.log "invoke config #{@cloud_id}", MU::WARN, details: invoke_cfg
345
+ # rescue ::Aws::Lambda::Errors::ResourceNotFoundException
346
+ # end
347
+
348
+ # MU.log @cloud_id, MU::WARN, details: cloud_desc if @cloud_id == "Espier-Scheduled-Scanner"
349
+ # MU.log "configuration #{@cloud_id}", MU::WARN, details: MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).get_function_configuration(function_name: @cloud_id) if @cloud_id == "Espier-Scheduled-Scanner"
350
+
337
351
 
338
352
  if function.code.repository_type == "S3"
339
353
  bok['code'] = {}
@@ -393,16 +407,29 @@ module MU
393
407
 
394
408
  if function.configuration.role
395
409
  shortname = function.configuration.role.sub(/.*?role\/([^\/]+)$/, '\1')
396
- MU.log shortname, MU::NOTICE, details: function.configuration.role
397
410
  bok['role'] = MU::Config::Ref.get(
398
411
  id: shortname,
399
- name: shortname,
400
412
  cloud: "AWS",
401
413
  type: "roles"
402
414
  )
403
415
  end
416
+
417
+ begin
418
+ pol = MU::Cloud::AWS.lambda(region: @region, credentials: @credentials).get_policy(function_name: @cloud_id).policy
419
+ MU.log @cloud_id, MU::WARN, details: JSON.parse(pol) if @cloud_id == "ESPIER-DEV-2020080900-LN-ON-DEMAND-SCANNER"
420
+ if pol
421
+ bok['triggers'] ||= []
422
+ JSON.parse(pol)["Statement"].each { |s|
423
+ bok['triggers'] << {
424
+ "service" => s["Principal"]["Service"].sub(/\..*/, ''),
425
+ "name" => s["Resource"].sub(/.*?[:\/]([^:\/]+)$/, '\1')
426
+ }
427
+ }
428
+ end
429
+ rescue ::Aws::Lambda::Errors::ResourceNotFoundException
430
+ end
404
431
  #MU.log @cloud_id, MU::NOTICE, details: function
405
- # XXX triggers, permissions
432
+ # XXX permissions
406
433
 
407
434
  bok
408
435
  end
@@ -414,6 +441,22 @@ MU.log shortname, MU::NOTICE, details: function.configuration.role
414
441
  def self.schema(_config)
415
442
  toplevel_required = ["runtime"]
416
443
  schema = {
444
+ "invoke_on_completion" => {
445
+ "type" => "object",
446
+ "description" => "Setting this will cause this Lambda function to be invoked when its groom phase is complete.",
447
+ "required" => ["invocation_type"],
448
+ "properties" => {
449
+ "invocation_type" => {
450
+ "type" => "string",
451
+ "enum" => ["RequestResponse", "Event", "Dryrun"],
452
+ "default" => "RequestReponse"
453
+ },
454
+ "payload" => {
455
+ "type" => "object",
456
+ "description" => "Optional input to the function, which will be formatted as JSON and sent for execution"
457
+ }
458
+ }
459
+ },
417
460
  "triggers" => {
418
461
  "type" => "array",
419
462
  "items" => {
@@ -423,7 +466,7 @@ MU.log shortname, MU::NOTICE, details: function.configuration.role
423
466
  "properties" => {
424
467
  "service" => {
425
468
  "type" => "string",
426
- "enum" => %w{apigateway events s3 sns sqs dynamodb kinesis ses cognito alexa iot},
469
+ "enum" => %w{apigateway events s3 sns sqs dynamodb kinesis ses cognito alexa iot lex},
427
470
  "description" => "The name of the AWS service that will trigger this function"
428
471
  },
429
472
  "name" => {
@@ -482,6 +525,28 @@ MU.log shortname, MU::NOTICE, details: function.configuration.role
482
525
  def self.validateConfig(function, configurator)
483
526
  ok = true
484
527
 
528
+ if function['triggers']
529
+ function['triggers'].each { |t|
530
+ mu_type = if t["service"] == "sns"
531
+ "notifiers"
532
+ elsif t["service"] == "apigateway"
533
+ "endpoints"
534
+ elsif t["service"] == "s3"
535
+ "buckets"
536
+ elsif t["service"] == "dynamodb"
537
+ "nosqldbs"
538
+ elsif t["service"] == "events"
539
+ "jobs"
540
+ elsif t["service"] == "sqs"
541
+ "msg_queues"
542
+ end
543
+
544
+ if mu_type
545
+ MU::Config.addDependency(function, t['name'], mu_type, my_phase: "groom")
546
+ end
547
+ }
548
+ end
549
+
485
550
  if function['vpc']
486
551
  fwname = "lambda-#{function['name']}"
487
552
  # default to allowing pings, if no ingress_rules were specified
@@ -508,7 +573,10 @@ MU.log shortname, MU::NOTICE, details: function.configuration.role
508
573
  MU::Config.addDependency(function, fwname, "firewall_rule")
509
574
  end
510
575
 
511
- if !function['iam_role']
576
+ function['role'] ||= function['iam_role']
577
+ function.delete("iam_role")
578
+
579
+ if !function['role']
512
580
  policy_map = {
513
581
  "basic" => "AWSLambdaBasicExecutionRole",
514
582
  "kinesis" => "AWSLambdaKinesisExecutionRole",
@@ -537,9 +605,21 @@ MU.log shortname, MU::NOTICE, details: function.configuration.role
537
605
  }
538
606
  configurator.insertKitten(roledesc, "roles")
539
607
 
540
- function['iam_role'] = function['name']+"execrole"
608
+ function['role'] = function['name']+"execrole"
541
609
 
542
- MU::Config.addDependency(function, function['name']+"execrole", "role")
610
+ end
611
+
612
+ if function['role'].is_a?(String)
613
+ function['role'] = MU::Config::Ref.get(
614
+ name: function['role'],
615
+ type: "roles",
616
+ cloud: "AWS",
617
+ credentials: function['credentials']
618
+ )
619
+ end
620
+
621
+ if function['role']['name']
622
+ MU::Config.addDependency(function, function['role']['name'], "role")
543
623
  end
544
624
 
545
625
  ok
@@ -547,23 +627,109 @@ MU.log shortname, MU::NOTICE, details: function.configuration.role
547
627
 
548
628
  private
549
629
 
550
- # Given an IAM role name, resolve to ARN. Will attempt to identify any
551
- # sibling Mu role resources by this name first, and failing that, will
552
- # do a plain get_role() to the IAM API for the provided name.
553
- # @param name [String]
554
- def get_role_arn(name)
555
- sib_role = @deploy.findLitterMate(name: name, type: "roles")
556
- return sib_role.cloudobj.arn if sib_role
630
+ def get_properties
631
+ role_obj = MU::Config::Ref.get(@config['role']).kitten(@deploy, cloud: "AWS")
632
+ raise MuError.new "Failed to fetch object from role reference", details: @config['role'].to_h if !role_obj
557
633
 
558
- begin
559
- role = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_role({
560
- role_name: name.to_s
561
- })
562
- return role['role']['arn']
563
- rescue StandardError => e
564
- MU.log "#{e}", MU::ERR
634
+ lambda_properties = {
635
+ code: {},
636
+ function_name: @mu_name,
637
+ handler: @config['handler'],
638
+ publish: true,
639
+ role: role_obj.arn,
640
+ runtime: @config['runtime'],
641
+ }
642
+
643
+ if @config['code']['zip_file'] or @config['code']['path']
644
+ tempfile = nil
645
+ if @config['code']['path']
646
+ tempfile = Tempfile.new
647
+ MU.log "#{@mu_name} using code at #{@config['code']['path']}"
648
+ MU::Master.zipDir(@config['code']['path'], tempfile.path)
649
+ @config['code']['zip_file'] = tempfile.path
650
+ else
651
+ MU.log "#{@mu_name} using code packaged at #{@config['code']['zip_file']}"
652
+ end
653
+ zip = File.read(@config['code']['zip_file'])
654
+ @code_sha256 = Base64.encode64(Digest::SHA256.digest(zip)).chomp
655
+ lambda_properties[:code][:zip_file] = zip
656
+ if tempfile
657
+ tempfile.close
658
+ tempfile.unlink
659
+ end
660
+ else
661
+ lambda_properties[:code][:s3_bucket] = @config['code']['s3_bucket']
662
+ lambda_properties[:code][:s3_key] = @config['code']['s3_key']
663
+ if @config['code']['s3_object_version']
664
+ lambda_properties[:code][:s3_object_version] = @config['code']['s3_object_version']
665
+ end
666
+ # XXX need to download to a temporarily file, read it in, and calculate the digest in order to trigger updates in groom
565
667
  end
566
- nil
668
+
669
+ if @config.has_key?('timeout')
670
+ lambda_properties[:timeout] = @config['timeout'].to_i ## secs
671
+ end
672
+
673
+ if @config.has_key?('memory')
674
+ lambda_properties[:memory_size] = @config['memory'].to_i
675
+ end
676
+
677
+ SIBLING_VARS.each_key { |sib_type|
678
+ siblings = @deploy.findLitterMate(return_all: true, type: sib_type, cloud: "AWS")
679
+ if siblings
680
+ siblings.each_value { |sibling|
681
+ metadata = sibling.notify
682
+ if !metadata
683
+ MU.log "Failed to extract metadata from sibling #{sibling}", MU::WARN
684
+ next
685
+ end
686
+ SIBLING_VARS[sib_type].each { |var|
687
+ if metadata[var]
688
+ @config['environment_variables'] ||= []
689
+ @config['environment_variables'] << {
690
+ "key" => (sibling.config['name']+"_"+var).gsub(/[^a-z0-9_]/i, '_'),
691
+ "value" => metadata[var]
692
+ }
693
+ end
694
+ }
695
+ }
696
+ end
697
+ }
698
+
699
+ if @config.has_key?('environment_variables')
700
+ lambda_properties[:environment] = {
701
+ variables: Hash[@config['environment_variables'].map { |v| [v['key'], v['value']] }]
702
+ }
703
+ end
704
+
705
+ lambda_properties[:tags] = {}
706
+ MU::MommaCat.listStandardTags.each_pair { |k, v|
707
+ lambda_properties[:tags][k] = v
708
+ }
709
+ if @config['tags']
710
+ @config['tags'].each { |tag|
711
+ lambda_properties[:tags][tag['key']] = tag['value']
712
+ }
713
+ end
714
+
715
+ if @config.has_key?('vpc')
716
+ sgs = []
717
+ if @config['add_firewall_rules']
718
+ @config['add_firewall_rules'].each { |sg|
719
+ sg = @deploy.findLitterMate(type: "firewall_rule", name: sg['name'])
720
+ sgs << sg.cloud_id if sg and sg.cloud_id
721
+ }
722
+ end
723
+ if !@vpc
724
+ raise MuError, "Function #{@config['name']} had a VPC configured, but none was loaded"
725
+ end
726
+ lambda_properties[:vpc_config] = {
727
+ :subnet_ids => @vpc.subnets.map { |s| s.cloud_id },
728
+ :security_group_ids => sgs
729
+ }
730
+ end
731
+
732
+ lambda_properties
567
733
  end
568
734
 
569
735
  end