cloud-mu 3.1.3 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +15 -3
- data/ansible/roles/mu-windows/README.md +33 -0
- data/ansible/roles/mu-windows/defaults/main.yml +2 -0
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/handlers/main.yml +2 -0
- data/ansible/roles/mu-windows/meta/main.yml +53 -0
- data/ansible/roles/mu-windows/tasks/main.yml +36 -0
- data/ansible/roles/mu-windows/tests/inventory +2 -0
- data/ansible/roles/mu-windows/tests/test.yml +5 -0
- data/ansible/roles/mu-windows/vars/main.yml +2 -0
- data/bin/mu-adopt +21 -13
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +52 -0
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-load-config.rb +4 -4
- data/bin/mu-node-manage +15 -16
- data/bin/mu-run-tests +147 -37
- data/cloud-mu.gemspec +22 -20
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/libraries/helper.rb +3 -2
- data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
- data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
- data/cookbooks/mu-tools/resources/disk.rb +1 -1
- data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
- data/extras/clean-stock-amis +25 -19
- data/extras/generate-stock-images +1 -0
- data/extras/image-generators/AWS/win2k12.yaml +18 -13
- data/extras/image-generators/AWS/win2k16.yaml +18 -13
- data/extras/image-generators/AWS/win2k19.yaml +21 -0
- data/extras/image-generators/Google/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +1 -1
- data/modules/mommacat.ru +6 -16
- data/modules/mu.rb +158 -111
- data/modules/mu/adoption.rb +404 -71
- data/modules/mu/cleanup.rb +221 -306
- data/modules/mu/cloud.rb +129 -1633
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +44 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +926 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +169 -0
- data/modules/mu/config.rb +171 -1767
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +32 -3
- data/modules/mu/config/cache_cluster.rb +2 -2
- data/modules/mu/config/cdn.rb +100 -0
- data/modules/mu/config/collection.rb +4 -4
- data/modules/mu/config/container_cluster.rb +9 -4
- data/modules/mu/config/database.rb +84 -105
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +10 -9
- data/modules/mu/config/doc_helpers.rb +516 -0
- data/modules/mu/config/endpoint.rb +5 -4
- data/modules/mu/config/firewall_rule.rb +103 -4
- data/modules/mu/config/folder.rb +4 -4
- data/modules/mu/config/function.rb +19 -10
- data/modules/mu/config/group.rb +4 -4
- data/modules/mu/config/habitat.rb +4 -4
- data/modules/mu/config/job.rb +89 -0
- data/modules/mu/config/loadbalancer.rb +60 -14
- data/modules/mu/config/log.rb +4 -4
- data/modules/mu/config/msg_queue.rb +4 -4
- data/modules/mu/config/nosqldb.rb +4 -4
- data/modules/mu/config/notifier.rb +10 -21
- data/modules/mu/config/ref.rb +411 -0
- data/modules/mu/config/role.rb +4 -4
- data/modules/mu/config/schema_helpers.rb +509 -0
- data/modules/mu/config/search_domain.rb +4 -4
- data/modules/mu/config/server.rb +98 -71
- data/modules/mu/config/server.yml +1 -0
- data/modules/mu/config/server_pool.rb +5 -9
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +200 -0
- data/modules/mu/config/user.rb +4 -4
- data/modules/mu/config/vpc.rb +71 -27
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +91 -68
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +3 -2
- data/modules/mu/deploy.rb +43 -26
- data/modules/mu/groomer.rb +17 -2
- data/modules/mu/groomers/ansible.rb +188 -41
- data/modules/mu/groomers/chef.rb +116 -55
- data/modules/mu/logger.rb +127 -148
- data/modules/mu/master.rb +410 -2
- data/modules/mu/master/chef.rb +3 -4
- data/modules/mu/master/ldap.rb +3 -3
- data/modules/mu/master/ssl.rb +12 -3
- data/modules/mu/mommacat.rb +218 -2612
- data/modules/mu/mommacat/daemon.rb +403 -0
- data/modules/mu/mommacat/naming.rb +473 -0
- data/modules/mu/mommacat/search.rb +495 -0
- data/modules/mu/mommacat/storage.rb +722 -0
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +380 -122
- data/modules/mu/{clouds → providers}/aws/alarm.rb +7 -5
- data/modules/mu/{clouds → providers}/aws/bucket.rb +297 -59
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +37 -71
- data/modules/mu/providers/aws/cdn.rb +782 -0
- data/modules/mu/{clouds → providers}/aws/collection.rb +26 -25
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +724 -744
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +88 -70
- data/modules/mu/providers/aws/endpoint.rb +1072 -0
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +220 -247
- data/modules/mu/{clouds → providers}/aws/folder.rb +8 -8
- data/modules/mu/{clouds → providers}/aws/function.rb +300 -142
- data/modules/mu/{clouds → providers}/aws/group.rb +31 -29
- data/modules/mu/{clouds → providers}/aws/habitat.rb +18 -15
- data/modules/mu/providers/aws/job.rb +466 -0
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +66 -56
- data/modules/mu/{clouds → providers}/aws/log.rb +17 -14
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +29 -19
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +114 -16
- data/modules/mu/{clouds → providers}/aws/notifier.rb +142 -65
- data/modules/mu/{clouds → providers}/aws/role.rb +158 -118
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +201 -59
- data/modules/mu/{clouds → providers}/aws/server.rb +844 -1139
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +74 -65
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +26 -44
- data/modules/mu/{clouds → providers}/aws/user.rb +24 -25
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
- data/modules/mu/{clouds → providers}/aws/vpc.rb +525 -931
- data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
- data/modules/mu/{clouds → providers}/azure.rb +29 -9
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
- data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
- data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
- data/modules/mu/{clouds → providers}/azure/server.rb +97 -49
- data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
- data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +68 -30
- data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +85 -78
- data/modules/mu/{clouds → providers}/google/database.rb +11 -21
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
- data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
- data/modules/mu/{clouds → providers}/google/function.rb +140 -168
- data/modules/mu/{clouds → providers}/google/group.rb +29 -34
- data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +19 -21
- data/modules/mu/{clouds → providers}/google/role.rb +94 -58
- data/modules/mu/{clouds → providers}/google/server.rb +243 -156
- data/modules/mu/{clouds → providers}/google/server_pool.rb +26 -45
- data/modules/mu/{clouds → providers}/google/user.rb +95 -31
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
- data/modules/tests/aws-jobs-functions.yaml +46 -0
- data/modules/tests/bucket.yml +4 -0
- data/modules/tests/centos6.yaml +15 -0
- data/modules/tests/centos7.yaml +15 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/ecs.yaml +23 -0
- data/modules/tests/eks.yaml +1 -1
- data/modules/tests/functions/node-function/lambda_function.js +10 -0
- data/modules/tests/functions/python-function/lambda_function.py +12 -0
- data/modules/tests/includes-and-params.yaml +2 -1
- data/modules/tests/microservice_app.yaml +288 -0
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/aws-iam.yaml +201 -0
- data/modules/tests/regrooms/bucket.yml +19 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/modules/tests/server-with-scrub-muisms.yaml +2 -1
- data/modules/tests/super_complex_bok.yml +2 -2
- data/modules/tests/super_simple_bok.yml +3 -5
- data/modules/tests/win2k12.yaml +17 -5
- data/modules/tests/win2k16.yaml +25 -0
- data/modules/tests/win2k19.yaml +25 -0
- data/requirements.txt +1 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +240 -154
- data/extras/image-generators/AWS/windows.yaml +0 -18
- data/modules/mu/clouds/aws/database.rb +0 -1985
- data/modules/mu/clouds/aws/endpoint.rb +0 -592
@@ -124,13 +124,15 @@ 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
|
+
MU.log "AWS::Alarm.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
129
|
+
MU.log "Placeholder: AWS Alarm artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
|
128
130
|
alarms = []
|
129
131
|
# We don't have a way to tag alarms, so we try to delete them by the deploy ID.
|
130
132
|
# This can miss alarms in some cases (eg. cache_cluster) so we might want to delete alarms from each API as well.
|
131
133
|
MU::Cloud::AWS.cloudwatch(credentials: credentials, region: region).describe_alarms.each { |page|
|
132
134
|
page.metric_alarms.map(&:alarm_name).each { |alarm_name|
|
133
|
-
alarms << alarm_name if alarm_name.match(
|
135
|
+
alarms << alarm_name if alarm_name.match(deploy_id)
|
134
136
|
}
|
135
137
|
}
|
136
138
|
|
@@ -252,9 +254,9 @@ module MU
|
|
252
254
|
end
|
253
255
|
|
254
256
|
# Cloud-specific configuration properties.
|
255
|
-
# @param
|
257
|
+
# @param _config [MU::Config]: The calling MU::Config object
|
256
258
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
257
|
-
def self.schema(
|
259
|
+
def self.schema(_config)
|
258
260
|
toplevel_required = []
|
259
261
|
schema = {}
|
260
262
|
[toplevel_required, schema]
|
@@ -319,7 +321,7 @@ module MU
|
|
319
321
|
if !depclass.nil?
|
320
322
|
dimension["depclass"] = depclass
|
321
323
|
if !dimension["name"].nil? and !dimension["name"].empty?
|
322
|
-
alarm
|
324
|
+
MU::Config.addDependency(alarm, dimension["name"], depclass)
|
323
325
|
end
|
324
326
|
end
|
325
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)
|
@@ -33,7 +39,7 @@ module MU
|
|
33
39
|
bucket_name = @deploy.getResourceName(@config["name"], max_length: 63).downcase
|
34
40
|
|
35
41
|
MU.log "Creating S3 bucket #{bucket_name}"
|
36
|
-
|
42
|
+
MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).create_bucket(
|
37
43
|
acl: @config['acl'],
|
38
44
|
bucket: bucket_name
|
39
45
|
)
|
@@ -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,21 +125,46 @@ module MU
|
|
90
125
|
tagBucket if !@config['scrub_mu_isms']
|
91
126
|
|
92
127
|
current = cloud_desc
|
128
|
+
applyPolicies if @config['policies']
|
93
129
|
|
94
|
-
if @config['
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
149
|
+
|
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
|
100
164
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_policy(
|
105
|
-
bucket: @cloud_id,
|
106
|
-
policy: JSON.generate(doc.values.first)
|
107
|
-
)
|
165
|
+
Hash[upload_me].each_pair { |file, url|
|
166
|
+
self.class.upload(url, file: file, credentials: @credentials, region: @config['region'], acl: batch['acl'])
|
167
|
+
}
|
108
168
|
}
|
109
169
|
end
|
110
170
|
|
@@ -121,6 +181,23 @@ module MU
|
|
121
181
|
}
|
122
182
|
}
|
123
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
|
124
201
|
elsif !@config['web'] and !current["website"].nil?
|
125
202
|
MU.log "Disabling web service on S3 bucket #{@cloud_id}", MU::NOTICE
|
126
203
|
MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).delete_bucket_website(
|
@@ -128,25 +205,38 @@ module MU
|
|
128
205
|
)
|
129
206
|
end
|
130
207
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
mfa_delete: "Disabled",
|
137
|
-
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)
|
138
213
|
}
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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(
|
143
229
|
bucket: @cloud_id,
|
144
|
-
|
145
|
-
|
146
|
-
status: "Suspended"
|
230
|
+
cors_configuration: {
|
231
|
+
cors_rules: symbolify_keys.call(@config['cors'])
|
147
232
|
}
|
148
233
|
)
|
149
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
|
150
240
|
end
|
151
241
|
|
152
242
|
# Upload a file to a bucket.
|
@@ -161,7 +251,7 @@ module MU
|
|
161
251
|
|
162
252
|
if file and !file.empty?
|
163
253
|
if !File.exist?(file) or !File.readable?(file)
|
164
|
-
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}"
|
165
255
|
else
|
166
256
|
data = File.read(file)
|
167
257
|
end
|
@@ -177,17 +267,20 @@ module MU
|
|
177
267
|
end
|
178
268
|
|
179
269
|
begin
|
180
|
-
puts data
|
181
|
-
puts acl
|
182
|
-
puts bucket
|
183
|
-
puts path
|
184
270
|
MU.log "Writing #{path} to S3 bucket #{bucket}"
|
185
|
-
|
271
|
+
params = {
|
186
272
|
acl: acl,
|
187
273
|
bucket: bucket,
|
188
274
|
key: path,
|
189
275
|
body: data
|
190
|
-
|
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)
|
191
284
|
rescue Aws::S3::Errors => e
|
192
285
|
raise MuError, "Got #{e.inspect} trying to write #{path} to #{bucket} (region: #{region}, credentials: #{credentials})"
|
193
286
|
end
|
@@ -212,7 +305,8 @@ puts path
|
|
212
305
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
213
306
|
# @param region [String]: The cloud provider region
|
214
307
|
# @return [void]
|
215
|
-
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: {})
|
309
|
+
MU.log "AWS::Bucket.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
216
310
|
|
217
311
|
resp = MU::Cloud::AWS.s3(credentials: credentials, region: region).list_buckets
|
218
312
|
if resp and resp.buckets
|
@@ -243,15 +337,36 @@ puts path
|
|
243
337
|
|
244
338
|
begin
|
245
339
|
tags = MU::Cloud::AWS.s3(credentials: credentials, region: region).get_bucket_tagging(bucket: bucket.name).tag_set
|
340
|
+
deploy_match = false
|
341
|
+
master_match = false
|
246
342
|
tags.each { |tag|
|
247
|
-
if tag.key == "MU-ID" and tag.value ==
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
end
|
252
|
-
break
|
343
|
+
if tag.key == "MU-ID" and tag.value == deploy_id
|
344
|
+
deploy_match = true
|
345
|
+
elsif tag.key == "MU-MASTER-IP" and tag.value == MU.mu_public_ip
|
346
|
+
master_match = true
|
253
347
|
end
|
254
348
|
}
|
349
|
+
if deploy_match and (ignoremaster or master_match)
|
350
|
+
MU.log "Deleting S3 Bucket #{bucket.name}"
|
351
|
+
if !noop
|
352
|
+
MU::Cloud::AWS.s3(credentials: credentials, region: region).delete_bucket(bucket: bucket.name)
|
353
|
+
end
|
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
|
255
370
|
rescue Aws::S3::Errors::NoSuchTagSet, Aws::S3::Errors::PermanentRedirect
|
256
371
|
next
|
257
372
|
end
|
@@ -277,9 +392,32 @@ puts path
|
|
277
392
|
def self.find(**args)
|
278
393
|
found = {}
|
279
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
|
+
|
280
415
|
if args[:cloud_id]
|
281
416
|
begin
|
282
|
-
found[args[:cloud_id]] = describe_bucket(args[:cloud_id], minimal:
|
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]
|
283
421
|
rescue ::Aws::S3::Errors::NoSuchBucket
|
284
422
|
end
|
285
423
|
else
|
@@ -287,11 +425,14 @@ puts path
|
|
287
425
|
if resp and resp.buckets
|
288
426
|
resp.buckets.each { |b|
|
289
427
|
begin
|
290
|
-
|
291
|
-
if !
|
428
|
+
bucket_region = location.call(b.name)
|
429
|
+
if !args[:allregions] and bucket_region != args[:region]
|
292
430
|
next
|
293
431
|
end
|
294
|
-
|
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
|
295
436
|
rescue Aws::S3::Errors::AccessDenied
|
296
437
|
end
|
297
438
|
}
|
@@ -301,22 +442,96 @@ puts path
|
|
301
442
|
found
|
302
443
|
end
|
303
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
|
+
|
304
467
|
# Cloud-specific configuration properties.
|
305
|
-
# @param
|
468
|
+
# @param _config [MU::Config]: The calling MU::Config object
|
306
469
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
307
|
-
def self.schema(
|
470
|
+
def self.schema(_config)
|
308
471
|
toplevel_required = []
|
309
472
|
schema = {
|
310
|
-
"policies" => MU::Cloud
|
311
|
-
"
|
312
|
-
"
|
313
|
-
|
314
|
-
|
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
|
+
}
|
315
484
|
},
|
316
485
|
"storage_class" => {
|
317
486
|
"type" => "string",
|
318
487
|
"enum" => ["STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA", "ONEZONE_IA", "INTELLIGENT_TIERING", "GLACIER"],
|
319
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
|
+
}
|
320
535
|
}
|
321
536
|
}
|
322
537
|
[toplevel_required, schema]
|
@@ -325,15 +540,15 @@ puts path
|
|
325
540
|
# Cloud-specific pre-processing of {MU::Config::BasketofKittens::bucket}, bare and unvalidated.
|
326
541
|
|
327
542
|
# @param bucket [Hash]: The resource to process and validate
|
328
|
-
# @param
|
543
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
329
544
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
330
|
-
def self.validateConfig(bucket,
|
545
|
+
def self.validateConfig(bucket, _configurator)
|
331
546
|
ok = true
|
332
547
|
|
333
548
|
if bucket['policies']
|
334
549
|
bucket['policies'].each { |pol|
|
335
550
|
if !pol['permissions'] or pol['permissions'].empty?
|
336
|
-
pol['permissions'] = ["s3:GetObject"]
|
551
|
+
pol['permissions'] = ["s3:GetObject", "s3:ListBucket"]
|
337
552
|
end
|
338
553
|
}
|
339
554
|
end
|
@@ -341,11 +556,13 @@ puts path
|
|
341
556
|
ok
|
342
557
|
end
|
343
558
|
|
344
|
-
private
|
345
|
-
|
346
559
|
# AWS doesn't really implement a useful describe_ method for S3 buckets;
|
347
560
|
# instead we run the million little individual API calls to construct
|
348
561
|
# an approximation for our uses
|
562
|
+
# @param bucket [String]:
|
563
|
+
# @param minimal [Boolean]:
|
564
|
+
# @param credentials [String]:
|
565
|
+
# @param region [String]:
|
349
566
|
def self.describe_bucket(bucket, minimal: false, credentials: nil, region: nil)
|
350
567
|
@@region_cache = {}
|
351
568
|
@@region_cache_semaphore = Mutex.new
|
@@ -372,7 +589,7 @@ puts path
|
|
372
589
|
}
|
373
590
|
end
|
374
591
|
|
375
|
-
rescue Aws::S3::Errors::NoSuchCORSConfiguration, Aws::S3::Errors::ServerSideEncryptionConfigurationNotFoundError, Aws::S3::Errors::NoSuchLifecycleConfiguration, Aws::S3::Errors::NoSuchBucketPolicy, Aws::S3::Errors::ReplicationConfigurationNotFoundError, Aws::S3::Errors::NoSuchTagSet, Aws::S3::Errors::NoSuchWebsiteConfiguration
|
592
|
+
rescue Aws::S3::Errors::NoSuchCORSConfiguration, Aws::S3::Errors::ServerSideEncryptionConfigurationNotFoundError, Aws::S3::Errors::NoSuchLifecycleConfiguration, Aws::S3::Errors::NoSuchBucketPolicy, Aws::S3::Errors::ReplicationConfigurationNotFoundError, Aws::S3::Errors::NoSuchTagSet, Aws::S3::Errors::NoSuchWebsiteConfiguration
|
376
593
|
desc[method] = nil
|
377
594
|
next
|
378
595
|
end
|
@@ -380,6 +597,27 @@ puts path
|
|
380
597
|
desc
|
381
598
|
end
|
382
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
|
+
|
383
621
|
end
|
384
622
|
end
|
385
623
|
end
|