cloud-mu 3.1.5 → 3.3.2

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  4. data/ansible/roles/mu-windows/files/config.xml +76 -0
  5. data/ansible/roles/mu-windows/tasks/main.yml +16 -0
  6. data/bin/mu-adopt +16 -12
  7. data/bin/mu-azure-tests +57 -0
  8. data/bin/mu-cleanup +2 -4
  9. data/bin/mu-configure +52 -0
  10. data/bin/mu-deploy +3 -3
  11. data/bin/mu-findstray-tests +25 -0
  12. data/bin/mu-gen-docs +2 -4
  13. data/bin/mu-load-config.rb +2 -1
  14. data/bin/mu-node-manage +15 -16
  15. data/bin/mu-run-tests +37 -12
  16. data/cloud-mu.gemspec +3 -3
  17. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  18. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  19. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  20. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  21. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  22. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  23. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  24. data/extras/clean-stock-amis +25 -19
  25. data/extras/generate-stock-images +1 -0
  26. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  27. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  28. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  29. data/modules/mommacat.ru +1 -1
  30. data/modules/mu.rb +86 -98
  31. data/modules/mu/adoption.rb +373 -58
  32. data/modules/mu/cleanup.rb +214 -303
  33. data/modules/mu/cloud.rb +128 -1733
  34. data/modules/mu/cloud/database.rb +49 -0
  35. data/modules/mu/cloud/dnszone.rb +44 -0
  36. data/modules/mu/cloud/machine_images.rb +212 -0
  37. data/modules/mu/cloud/providers.rb +81 -0
  38. data/modules/mu/cloud/resource_base.rb +929 -0
  39. data/modules/mu/cloud/server.rb +40 -0
  40. data/modules/mu/cloud/server_pool.rb +1 -0
  41. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  42. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  43. data/modules/mu/cloud/wrappers.rb +169 -0
  44. data/modules/mu/config.rb +123 -81
  45. data/modules/mu/config/alarm.rb +2 -6
  46. data/modules/mu/config/bucket.rb +32 -3
  47. data/modules/mu/config/cache_cluster.rb +2 -2
  48. data/modules/mu/config/cdn.rb +100 -0
  49. data/modules/mu/config/collection.rb +1 -1
  50. data/modules/mu/config/container_cluster.rb +7 -2
  51. data/modules/mu/config/database.rb +84 -105
  52. data/modules/mu/config/database.yml +1 -2
  53. data/modules/mu/config/dnszone.rb +5 -4
  54. data/modules/mu/config/doc_helpers.rb +5 -6
  55. data/modules/mu/config/endpoint.rb +2 -1
  56. data/modules/mu/config/firewall_rule.rb +3 -19
  57. data/modules/mu/config/folder.rb +1 -1
  58. data/modules/mu/config/function.rb +17 -8
  59. data/modules/mu/config/group.rb +1 -1
  60. data/modules/mu/config/habitat.rb +1 -1
  61. data/modules/mu/config/job.rb +89 -0
  62. data/modules/mu/config/loadbalancer.rb +57 -11
  63. data/modules/mu/config/log.rb +1 -1
  64. data/modules/mu/config/msg_queue.rb +1 -1
  65. data/modules/mu/config/nosqldb.rb +1 -1
  66. data/modules/mu/config/notifier.rb +8 -19
  67. data/modules/mu/config/ref.rb +92 -14
  68. data/modules/mu/config/role.rb +1 -1
  69. data/modules/mu/config/schema_helpers.rb +38 -37
  70. data/modules/mu/config/search_domain.rb +1 -1
  71. data/modules/mu/config/server.rb +12 -13
  72. data/modules/mu/config/server_pool.rb +3 -7
  73. data/modules/mu/config/storage_pool.rb +1 -1
  74. data/modules/mu/config/tail.rb +11 -0
  75. data/modules/mu/config/user.rb +1 -1
  76. data/modules/mu/config/vpc.rb +27 -23
  77. data/modules/mu/config/vpc.yml +0 -1
  78. data/modules/mu/defaults/AWS.yaml +90 -90
  79. data/modules/mu/defaults/Azure.yaml +1 -0
  80. data/modules/mu/defaults/Google.yaml +1 -0
  81. data/modules/mu/deploy.rb +34 -20
  82. data/modules/mu/groomer.rb +16 -1
  83. data/modules/mu/groomers/ansible.rb +69 -4
  84. data/modules/mu/groomers/chef.rb +51 -4
  85. data/modules/mu/logger.rb +120 -144
  86. data/modules/mu/master.rb +97 -4
  87. data/modules/mu/mommacat.rb +160 -874
  88. data/modules/mu/mommacat/daemon.rb +23 -14
  89. data/modules/mu/mommacat/naming.rb +110 -3
  90. data/modules/mu/mommacat/search.rb +497 -0
  91. data/modules/mu/mommacat/storage.rb +252 -194
  92. data/modules/mu/{clouds → providers}/README.md +1 -1
  93. data/modules/mu/{clouds → providers}/aws.rb +258 -57
  94. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  95. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  96. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  99. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +95 -84
  100. data/modules/mu/providers/aws/database.rb +1744 -0
  101. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  102. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  103. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  104. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  105. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  106. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  107. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  108. data/modules/mu/providers/aws/job.rb +466 -0
  109. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  110. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  111. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  112. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  113. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  114. data/modules/mu/{clouds → providers}/aws/role.rb +76 -48
  115. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  116. data/modules/mu/{clouds → providers}/aws/server.rb +66 -98
  117. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  118. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  119. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  120. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  121. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  122. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  123. data/modules/mu/{clouds → providers}/aws/vpc.rb +143 -74
  124. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  126. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  127. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  128. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  129. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  130. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  132. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  133. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  134. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  135. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  136. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  137. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  138. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  142. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  143. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  144. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  145. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  146. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  147. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  148. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  149. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  150. data/modules/mu/{clouds → providers}/google.rb +29 -6
  151. data/modules/mu/{clouds → providers}/google/bucket.rb +4 -4
  152. data/modules/mu/{clouds → providers}/google/container_cluster.rb +38 -20
  153. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  154. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  155. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  156. data/modules/mu/{clouds → providers}/google/function.rb +6 -6
  157. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  158. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  159. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  160. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  161. data/modules/mu/{clouds → providers}/google/server.rb +41 -24
  162. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  163. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  164. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  165. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  166. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  167. data/modules/mu/{clouds → providers}/google/vpc.rb +45 -14
  168. data/modules/tests/aws-jobs-functions.yaml +46 -0
  169. data/modules/tests/centos6.yaml +15 -0
  170. data/modules/tests/centos7.yaml +15 -0
  171. data/modules/tests/centos8.yaml +12 -0
  172. data/modules/tests/ecs.yaml +2 -2
  173. data/modules/tests/eks.yaml +1 -1
  174. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  175. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  176. data/modules/tests/microservice_app.yaml +288 -0
  177. data/modules/tests/rds.yaml +108 -0
  178. data/modules/tests/regrooms/rds.yaml +123 -0
  179. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  180. data/modules/tests/super_complex_bok.yml +2 -2
  181. data/modules/tests/super_simple_bok.yml +3 -5
  182. data/spec/mu/clouds/azure_spec.rb +2 -2
  183. metadata +122 -92
  184. data/modules/mu/clouds/aws/database.rb +0 -1974
  185. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -124,7 +124,7 @@ module MU
124
124
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
125
125
  # @param region [String]: The cloud provider region
126
126
  # @return [void]
127
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
127
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
128
128
  MU.log "AWS::Alarm.cleanup: need to support flags['known']", MU::DEBUG, details: flags
129
129
  MU.log "Placeholder: AWS Alarm artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
130
130
  alarms = []
@@ -132,7 +132,7 @@ module MU
132
132
  # This can miss alarms in some cases (eg. cache_cluster) so we might want to delete alarms from each API as well.
133
133
  MU::Cloud::AWS.cloudwatch(credentials: credentials, region: region).describe_alarms.each { |page|
134
134
  page.metric_alarms.map(&:alarm_name).each { |alarm_name|
135
- alarms << alarm_name if alarm_name.match(MU.deploy_id)
135
+ alarms << alarm_name if alarm_name.match(deploy_id)
136
136
  }
137
137
  }
138
138
 
@@ -321,7 +321,7 @@ module MU
321
321
  if !depclass.nil?
322
322
  dimension["depclass"] = depclass
323
323
  if !dimension["name"].nil? and !dimension["name"].empty?
324
- alarm["dependencies"] << { "name" => dimension["name"], "type" => depclass }
324
+ MU::Config.addDependency(alarm, dimension["name"], depclass)
325
325
  end
326
326
  end
327
327
  }
@@ -21,6 +21,12 @@ module MU
21
21
  @@region_cache = {}
22
22
  @@region_cache_semaphore = Mutex.new
23
23
 
24
+ # Map some filename extensions to mime types. S3 does most of this on
25
+ # its own, add to this for cases it doesn't cover.
26
+ MIME_MAP = {
27
+ ".svg" => "image/svg+xml"
28
+ }
29
+
24
30
  # 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.
25
31
  # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
26
32
  def initialize(**args)
@@ -81,6 +87,35 @@ module MU
81
87
 
82
88
  end
83
89
 
90
+ # @return [String]
91
+ def url
92
+ "https://#{@cloud_id}.s3.amazonaws.com"
93
+ end
94
+
95
+ # Grant access via our bucket policy to the specified resource
96
+ # @param principal [String]
97
+ # @param permissions [Array<String>]
98
+ # @param paths [Array<String>]
99
+ def allowPrincipal(principal, permissions: ["GetObject", "ListBucket"], paths: [""], doc_id: nil, name: nil)
100
+ @config['policies'] ||= []
101
+ name ||= principal.sub(/.*?([0-9a-z\-_]+)$/i, '\1')
102
+ @config['policies'] << {
103
+ "name" => name,
104
+ "grant_to" => [ { "identifier" => principal } ],
105
+ "permissions" => permissions.map { |p| "s3:"+p },
106
+ "flag" => "allow",
107
+ "targets" => paths.map { |p|
108
+ {
109
+ "path" => p,
110
+ "type" => "bucket",
111
+ "identifier" => @config['name']
112
+ }
113
+ }
114
+ }
115
+
116
+ applyPolicies(doc_id: doc_id)
117
+ end
118
+
84
119
  # Called automatically by {MU::Deploy#createResources}
85
120
  def groom
86
121
 
@@ -90,20 +125,46 @@ module MU
90
125
  tagBucket if !@config['scrub_mu_isms']
91
126
 
92
127
  current = cloud_desc
93
- if @config['policies']
94
- @config['policies'].each { |pol|
95
- pol['grant_to'] ||= [
96
- { "id" => "*" }
97
- ]
98
- }
128
+ applyPolicies if @config['policies']
129
+
130
+ if @config['versioning'] and current["versioning"].status != "Enabled"
131
+ MU.log "Enabling versioning on S3 bucket #{@cloud_id}", MU::NOTICE
132
+ MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_versioning(
133
+ bucket: @cloud_id,
134
+ versioning_configuration: {
135
+ mfa_delete: "Disabled",
136
+ status: "Enabled"
137
+ }
138
+ )
139
+ elsif !@config['versioning'] and current["versioning"].status == "Enabled"
140
+ MU.log "Suspending versioning on S3 bucket #{@cloud_id}", MU::NOTICE
141
+ MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_versioning(
142
+ bucket: @cloud_id,
143
+ versioning_configuration: {
144
+ mfa_delete: "Disabled",
145
+ status: "Suspended"
146
+ }
147
+ )
148
+ end
99
149
 
100
- policy_docs = MU::Cloud::AWS::Role.genPolicyDocument(@config['policies'], deploy_obj: @deploy, bucket_style: true)
101
- policy_docs.each { |doc|
102
- MU.log "Applying S3 bucket policy #{doc.keys.first} to bucket #{@cloud_id}", MU::NOTICE, details: JSON.pretty_generate(doc.values.first)
103
- MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_policy(
104
- bucket: @cloud_id,
105
- policy: JSON.generate(doc.values.first)
106
- )
150
+ if @config['upload']
151
+ @config['upload'].each { |batch|
152
+ urlbase = "s3://"+@cloud_id+batch['destination']
153
+ urlbase += "/" if urlbase !~ /\/$/
154
+ upload_me = if File.directory?(batch['source'])
155
+ Dir[batch['source']+'/**/*'].reject {|d|
156
+ File.directory?(d)
157
+ }.map { |f|
158
+ [ f, urlbase+f.sub(/^#{Regexp.quote(batch['source'])}\/?/, '') ]
159
+ }
160
+ else
161
+ batch['source'].match(/([^\/]+)$/)
162
+ [ [batch['source'], urlbase+Regexp.last_match[1]] ]
163
+ end
164
+
165
+ Hash[upload_me].each_pair { |file, url|
166
+ self.class.upload(url, file: file, credentials: @credentials, region: @config['region'], acl: batch['acl'])
167
+ }
107
168
  }
108
169
  end
109
170
 
@@ -120,6 +181,23 @@ module MU
120
181
  }
121
182
  }
122
183
  )
184
+ ['web_error_object', 'web_index_object'].each { |key|
185
+ begin
186
+ MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).head_object(
187
+ bucket: @cloud_id,
188
+ key: @config[key]
189
+ )
190
+ rescue Aws::S3::Errors::NotFound
191
+ MU.log "Uploading placeholder #{@config[key]} to bucket #{@cloud_id}"
192
+ MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_object(
193
+ acl: "public-read",
194
+ bucket: @cloud_id,
195
+ key: @config[key],
196
+ body: ""
197
+ )
198
+ end
199
+ }
200
+ # XXX check if error and index objs exist, and if not provide placeholders
123
201
  elsif !@config['web'] and !current["website"].nil?
124
202
  MU.log "Disabling web service on S3 bucket #{@cloud_id}", MU::NOTICE
125
203
  MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).delete_bucket_website(
@@ -127,25 +205,38 @@ module MU
127
205
  )
128
206
  end
129
207
 
130
- if @config['versioning'] and current["versioning"].status != "Enabled"
131
- MU.log "Enabling versioning on S3 bucket #{@cloud_id}", MU::NOTICE
132
- MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_versioning(
133
- bucket: @cloud_id,
134
- versioning_configuration: {
135
- mfa_delete: "Disabled",
136
- status: "Enabled"
208
+ symbolify_keys = Proc.new { |parent|
209
+ if parent.is_a?(Hash)
210
+ newhash = {}
211
+ parent.each_pair { |k, v|
212
+ newhash[k.to_sym] = symbolify_keys.call(v)
137
213
  }
138
- )
139
- elsif !@config['versioning'] and current["versioning"].status == "Enabled"
140
- MU.log "Suspending versioning on S3 bucket #{@cloud_id}", MU::NOTICE
141
- MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_versioning(
214
+ newhash
215
+ elsif parent.is_a?(Array)
216
+ newarr = []
217
+ parent.each { |child|
218
+ newarr << symbolify_keys.call(child)
219
+ }
220
+ newarr
221
+ else
222
+ parent
223
+ end
224
+ }
225
+
226
+ if @config['cors']
227
+ MU.log "Setting CORS rules on #{@cloud_id}", details: @config['cors']
228
+ MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_cors(
142
229
  bucket: @cloud_id,
143
- versioning_configuration: {
144
- mfa_delete: "Disabled",
145
- status: "Suspended"
230
+ cors_configuration: {
231
+ cors_rules: symbolify_keys.call(@config['cors'])
146
232
  }
147
233
  )
148
234
  end
235
+
236
+ MU.log "Bucket #{@config['name']}: s3://#{@cloud_id}", MU::SUMMARY
237
+ if @config['web']
238
+ MU.log "Bucket #{@config['name']} web access: http://#{@cloud_id}.s3-website-#{@config['region']}.amazonaws.com/", MU::SUMMARY
239
+ end
149
240
  end
150
241
 
151
242
  # Upload a file to a bucket.
@@ -160,7 +251,7 @@ module MU
160
251
 
161
252
  if file and !file.empty?
162
253
  if !File.exist?(file) or !File.readable?(file)
163
- raise MuError, "Unable to read #{file} for upload to #{url}"
254
+ raise MuError, "Unable to read #{file} for upload to #{url} (I'm at #{Dir.pwd}"
164
255
  else
165
256
  data = File.read(file)
166
257
  end
@@ -177,12 +268,19 @@ module MU
177
268
 
178
269
  begin
179
270
  MU.log "Writing #{path} to S3 bucket #{bucket}"
180
- MU::Cloud::AWS.s3(region: region, credentials: credentials).put_object(
271
+ params = {
181
272
  acl: acl,
182
273
  bucket: bucket,
183
274
  key: path,
184
275
  body: data
185
- )
276
+ }
277
+
278
+ MIME_MAP.each_pair { |extension, content_type|
279
+ if path =~ /#{Regexp.quote(extension)}$/i
280
+ params[:content_type] = content_type
281
+ end
282
+ }
283
+ MU::Cloud::AWS.s3(region: region, credentials: credentials).put_object(params)
186
284
  rescue Aws::S3::Errors => e
187
285
  raise MuError, "Got #{e.inspect} trying to write #{path} to #{bucket} (region: #{region}, credentials: #{credentials})"
188
286
  end
@@ -207,7 +305,7 @@ module MU
207
305
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
208
306
  # @param region [String]: The cloud provider region
209
307
  # @return [void]
210
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
308
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
211
309
  MU.log "AWS::Bucket.cleanup: need to support flags['known']", MU::DEBUG, details: flags
212
310
 
213
311
  resp = MU::Cloud::AWS.s3(credentials: credentials, region: region).list_buckets
@@ -242,7 +340,7 @@ module MU
242
340
  deploy_match = false
243
341
  master_match = false
244
342
  tags.each { |tag|
245
- if tag.key == "MU-ID" and tag.value == MU.deploy_id
343
+ if tag.key == "MU-ID" and tag.value == deploy_id
246
344
  deploy_match = true
247
345
  elsif tag.key == "MU-MASTER-IP" and tag.value == MU.mu_public_ip
248
346
  master_match = true
@@ -254,6 +352,21 @@ module MU
254
352
  MU::Cloud::AWS.s3(credentials: credentials, region: region).delete_bucket(bucket: bucket.name)
255
353
  end
256
354
  end
355
+ rescue Aws::S3::Errors::BucketNotEmpty => e
356
+ if flags["skipsnapshots"]
357
+ del = MU::Cloud::AWS.s3(credentials: credentials, region: region).list_objects(bucket: bucket.name).contents.map { |o| { key: o.key } }
358
+ del.concat(MU::Cloud::AWS.s3(credentials: credentials, region: region).list_object_versions(bucket: bucket.name).versions.map { |o| { key: o.key, version_id: o.version_id } })
359
+
360
+ MU.log "Purging #{del.size.to_s} objects and versions from #{bucket.name}"
361
+ begin
362
+ batch = del.slice!(0, (del.length >= 1000 ? 1000 : del.length))
363
+ MU::Cloud::AWS.s3(credentials: credentials, region: region).delete_objects(bucket: bucket.name, delete: { objects: batch } ) if !noop
364
+ end while del.size > 0
365
+
366
+ retry if !noop
367
+ else
368
+ MU.log "Bucket #{bucket.name} is non-empty, will preserve it and its contents. Use --skipsnapshots to forcibly remove.", MU::WARN
369
+ end
257
370
  rescue Aws::S3::Errors::NoSuchTagSet, Aws::S3::Errors::PermanentRedirect
258
371
  next
259
372
  end
@@ -279,9 +392,32 @@ module MU
279
392
  def self.find(**args)
280
393
  found = {}
281
394
 
395
+ args[:region] ||= MU::Cloud::AWS.myRegion(args[:credentials])
396
+ if args[:flags] and args[:flags][:allregions]
397
+ args[:allregions] = args[:flags][:allregions]
398
+ end
399
+ minimal = args[:full] ? false : true
400
+
401
+ location = Proc.new { |name|
402
+ begin
403
+ loc_resp = MU::Cloud::AWS.s3(credentials: args[:credentials], region: args[:region]).get_bucket_location(bucket: name)
404
+
405
+ if loc_resp.location_constraint and !loc_resp.location_constraint.empty?
406
+ loc_resp.location_constraint
407
+ else
408
+ nil
409
+ end
410
+ rescue Aws::S3::Errors::AccessDenied
411
+ nil
412
+ end
413
+ }
414
+
282
415
  if args[:cloud_id]
283
416
  begin
284
- found[args[:cloud_id]] = describe_bucket(args[:cloud_id], minimal: true, credentials: args[:credentials], region: args[:region])
417
+ found[args[:cloud_id]] = describe_bucket(args[:cloud_id], minimal: minimal, credentials: args[:credentials], region: args[:region])
418
+ found[args[:cloud_id]]['region'] ||= location.call(args[:cloud_id])
419
+ found[args[:cloud_id]]['region'] ||= args[:region]
420
+ found[args[:cloud_id]]['name'] ||= args[:cloud_id]
285
421
  rescue ::Aws::S3::Errors::NoSuchBucket
286
422
  end
287
423
  else
@@ -289,11 +425,14 @@ module MU
289
425
  if resp and resp.buckets
290
426
  resp.buckets.each { |b|
291
427
  begin
292
- loc_resp = MU::Cloud::AWS.s3(credentials: args[:credentials], region: args[:region]).get_bucket_location(bucket: b.name)
293
- if !loc_resp or loc_resp.location_constraint != args[:region]
428
+ bucket_region = location.call(b.name)
429
+ if !args[:allregions] and bucket_region != args[:region]
294
430
  next
295
431
  end
296
- found[b.name] = describe_bucket(b.name, minimal: true, credentials: args[:credentials], region: args[:region])
432
+ bucket_region ||= args[:region]
433
+ found[b.name] = describe_bucket(b.name, minimal: minimal, credentials: args[:credentials], region: bucket_region)
434
+ found[b.name]["region"] ||= bucket_region
435
+ found[b.name]['name'] ||= b.name
297
436
  rescue Aws::S3::Errors::AccessDenied
298
437
  end
299
438
  }
@@ -303,22 +442,96 @@ module MU
303
442
  found
304
443
  end
305
444
 
445
+ # Reverse-map our cloud description into a runnable config hash.
446
+ # We assume that any values we have in +@config+ are placeholders, and
447
+ # calculate our own accordingly based on what's live in the cloud.
448
+ def toKitten(**_args)
449
+ bok = {
450
+ "cloud" => "AWS",
451
+ "credentials" => @config['credentials'],
452
+ "cloud_id" => @cloud_id
453
+ }
454
+
455
+ if @cloud_id =~ /espier/i
456
+ MU.log @cloud_id, MU::WARN, details: cloud_desc
457
+ end
458
+
459
+ if !cloud_desc
460
+ MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
461
+ return nil
462
+ end
463
+
464
+ nil
465
+ end
466
+
306
467
  # Cloud-specific configuration properties.
307
468
  # @param _config [MU::Config]: The calling MU::Config object
308
469
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
309
470
  def self.schema(_config)
310
471
  toplevel_required = []
311
472
  schema = {
312
- "policies" => MU::Cloud::AWS::Role.condition_schema,
313
- "acl" => {
314
- "type" => "string",
315
- "enum" => ["private", "public-read", "public-read-write", "authenticated-read"],
316
- "default" => "private"
473
+ "policies" => MU::Cloud.resourceClass("AWS", "Role").condition_schema,
474
+ "upload" => {
475
+ "items" => {
476
+ "properties" => {
477
+ "acl" => {
478
+ "type" => "string",
479
+ "enum" => ["private", "public-read", "public-read-write", "authenticated-read"],
480
+ "default" => "private"
481
+ }
482
+ }
483
+ }
317
484
  },
318
485
  "storage_class" => {
319
486
  "type" => "string",
320
487
  "enum" => ["STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA", "ONEZONE_IA", "INTELLIGENT_TIERING", "GLACIER"],
321
488
  "default" => "STANDARD"
489
+ },
490
+ "cors" => {
491
+ "type" => "array",
492
+ "items" => {
493
+ "type" => "object",
494
+ "description" => "AWS S3 Cross-origin resource sharing policy",
495
+ "required" => ["allowed_origins"],
496
+ "properties" => {
497
+ "allowed_headers" => {
498
+ "type" => "array",
499
+ "default" => ["*"],
500
+ "items" => {
501
+ "type" => "string",
502
+ "description" => "Specifies which headers are allowed in a preflight request through the +Access-Control-Request-Headers+ header."
503
+ }
504
+ },
505
+ "allowed_methods" => {
506
+ "type" => "array",
507
+ "default" => ["GET"],
508
+ "items" => {
509
+ "type" => "string",
510
+ "enum" => %w{GET PUT POST DELETE HEAD},
511
+ "description" => "Specifies which HTTP methods for which cross-domain request are permitted"
512
+ }
513
+ },
514
+ "allowed_origins" => {
515
+ "type" => "array",
516
+ "items" => {
517
+ "type" => "string",
518
+ "description" => "Origins (in URL form) for which cross-domain request are permitted"
519
+ }
520
+ },
521
+ "expose_headers" => {
522
+ "type" => "array",
523
+ "items" => {
524
+ "type" => "string",
525
+ "description" => "Headers in the response which should be visible to the requesting application"
526
+ }
527
+ },
528
+ "max_age_seconds" => {
529
+ "type" => "integer",
530
+ "default" => 3600,
531
+ "description" => "Maximum cache time for preflight requests"
532
+ }
533
+ }
534
+ }
322
535
  }
323
536
  }
324
537
  [toplevel_required, schema]
@@ -384,6 +597,27 @@ module MU
384
597
  desc
385
598
  end
386
599
 
600
+ private
601
+
602
+ def applyPolicies(doc_id: nil)
603
+ return if !@config['policies']
604
+
605
+ @config['policies'].each { |pol|
606
+ pol['grant_to'] ||= [
607
+ { "id" => "*" }
608
+ ]
609
+ }
610
+
611
+ policy_docs = MU::Cloud.resourceClass("AWS", "Role").genPolicyDocument(@config['policies'], deploy_obj: @deploy, bucket_style: true, version: "2008-10-17", doc_id: doc_id)
612
+ policy_docs.each { |doc|
613
+ MU.log "Applying S3 bucket policy #{doc.keys.first} to bucket #{@cloud_id}", MU::NOTICE, details: JSON.pretty_generate(doc.values.first)
614
+ MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_policy(
615
+ bucket: @cloud_id,
616
+ policy: JSON.generate(doc.values.first)
617
+ )
618
+ }
619
+ end
620
+
387
621
  end
388
622
  end
389
623
  end