cloud-mu 3.2.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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