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