cloud-mu 3.1.4 → 3.3.1

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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +16 -12
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -1
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +37 -12
  23. data/cloud-mu.gemspec +5 -3
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  27. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  28. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  29. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  30. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  31. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  32. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  33. data/extras/clean-stock-amis +25 -19
  34. data/extras/generate-stock-images +1 -0
  35. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  36. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  37. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  38. data/modules/mommacat.ru +1 -1
  39. data/modules/mu.rb +158 -107
  40. data/modules/mu/adoption.rb +386 -59
  41. data/modules/mu/cleanup.rb +214 -303
  42. data/modules/mu/cloud.rb +128 -1632
  43. data/modules/mu/cloud/database.rb +49 -0
  44. data/modules/mu/cloud/dnszone.rb +44 -0
  45. data/modules/mu/cloud/machine_images.rb +212 -0
  46. data/modules/mu/cloud/providers.rb +81 -0
  47. data/modules/mu/cloud/resource_base.rb +926 -0
  48. data/modules/mu/cloud/server.rb +40 -0
  49. data/modules/mu/cloud/server_pool.rb +1 -0
  50. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  51. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  52. data/modules/mu/cloud/wrappers.rb +169 -0
  53. data/modules/mu/config.rb +135 -82
  54. data/modules/mu/config/alarm.rb +2 -6
  55. data/modules/mu/config/bucket.rb +32 -3
  56. data/modules/mu/config/cache_cluster.rb +2 -2
  57. data/modules/mu/config/cdn.rb +100 -0
  58. data/modules/mu/config/collection.rb +1 -1
  59. data/modules/mu/config/container_cluster.rb +7 -2
  60. data/modules/mu/config/database.rb +84 -105
  61. data/modules/mu/config/database.yml +1 -2
  62. data/modules/mu/config/dnszone.rb +5 -4
  63. data/modules/mu/config/doc_helpers.rb +5 -6
  64. data/modules/mu/config/endpoint.rb +2 -1
  65. data/modules/mu/config/firewall_rule.rb +3 -19
  66. data/modules/mu/config/folder.rb +1 -1
  67. data/modules/mu/config/function.rb +17 -8
  68. data/modules/mu/config/group.rb +1 -1
  69. data/modules/mu/config/habitat.rb +1 -1
  70. data/modules/mu/config/job.rb +89 -0
  71. data/modules/mu/config/loadbalancer.rb +57 -11
  72. data/modules/mu/config/log.rb +1 -1
  73. data/modules/mu/config/msg_queue.rb +1 -1
  74. data/modules/mu/config/nosqldb.rb +1 -1
  75. data/modules/mu/config/notifier.rb +8 -19
  76. data/modules/mu/config/ref.rb +92 -14
  77. data/modules/mu/config/role.rb +1 -1
  78. data/modules/mu/config/schema_helpers.rb +38 -37
  79. data/modules/mu/config/search_domain.rb +1 -1
  80. data/modules/mu/config/server.rb +12 -13
  81. data/modules/mu/config/server.yml +1 -0
  82. data/modules/mu/config/server_pool.rb +3 -7
  83. data/modules/mu/config/storage_pool.rb +1 -1
  84. data/modules/mu/config/tail.rb +11 -0
  85. data/modules/mu/config/user.rb +1 -1
  86. data/modules/mu/config/vpc.rb +27 -23
  87. data/modules/mu/config/vpc.yml +0 -1
  88. data/modules/mu/defaults/AWS.yaml +91 -68
  89. data/modules/mu/defaults/Azure.yaml +1 -0
  90. data/modules/mu/defaults/Google.yaml +1 -0
  91. data/modules/mu/deploy.rb +33 -19
  92. data/modules/mu/groomer.rb +16 -1
  93. data/modules/mu/groomers/ansible.rb +123 -21
  94. data/modules/mu/groomers/chef.rb +64 -11
  95. data/modules/mu/logger.rb +120 -144
  96. data/modules/mu/master.rb +97 -4
  97. data/modules/mu/master/ssl.rb +0 -1
  98. data/modules/mu/mommacat.rb +154 -867
  99. data/modules/mu/mommacat/daemon.rb +23 -14
  100. data/modules/mu/mommacat/naming.rb +110 -3
  101. data/modules/mu/mommacat/search.rb +495 -0
  102. data/modules/mu/mommacat/storage.rb +225 -192
  103. data/modules/mu/{clouds → providers}/README.md +1 -1
  104. data/modules/mu/{clouds → providers}/aws.rb +281 -64
  105. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  106. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  107. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  108. data/modules/mu/providers/aws/cdn.rb +782 -0
  109. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  110. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
  111. data/modules/mu/providers/aws/database.rb +1744 -0
  112. data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
  113. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  114. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
  115. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  116. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  117. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  118. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  119. data/modules/mu/providers/aws/job.rb +466 -0
  120. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
  121. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  122. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  123. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  124. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  125. data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
  126. data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
  127. data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
  128. data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
  129. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  130. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  131. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  132. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  133. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  134. data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
  135. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  136. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  137. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  138. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  139. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  140. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  141. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  142. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  143. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  144. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  145. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  146. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  147. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  148. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  149. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  150. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  151. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  152. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  153. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  156. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  158. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  159. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  160. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  161. data/modules/mu/{clouds → providers}/google.rb +29 -6
  162. data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
  163. data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
  164. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  165. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  166. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  167. data/modules/mu/{clouds → providers}/google/function.rb +14 -8
  168. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  169. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  170. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  171. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  172. data/modules/mu/{clouds → providers}/google/server.rb +142 -55
  173. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  174. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  175. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  176. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  177. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  178. data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
  179. data/modules/tests/aws-jobs-functions.yaml +46 -0
  180. data/modules/tests/centos6.yaml +15 -0
  181. data/modules/tests/centos7.yaml +15 -0
  182. data/modules/tests/centos8.yaml +12 -0
  183. data/modules/tests/ecs.yaml +23 -0
  184. data/modules/tests/eks.yaml +1 -1
  185. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  186. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/microservice_app.yaml +288 -0
  189. data/modules/tests/rds.yaml +108 -0
  190. data/modules/tests/regrooms/rds.yaml +123 -0
  191. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  192. data/modules/tests/super_complex_bok.yml +2 -2
  193. data/modules/tests/super_simple_bok.yml +3 -5
  194. data/modules/tests/win2k12.yaml +25 -0
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +169 -93
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1974
  202. data/modules/mu/clouds/aws/endpoint.rb +0 -596
  203. data/modules/tests/needwork/win2k12.yaml +0 -13
@@ -0,0 +1,286 @@
1
+ # Copyright:: Copyright (c) 2020 eGlobalTech, Inc., all rights reserved
2
+ #
3
+ # Licensed under the BSD-3 license (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the root of the project or at
6
+ #
7
+ # http://egt-labs.com/mu/LICENSE.html
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module MU
16
+ class Cloud
17
+ class AWS
18
+
19
+ # Creation of Virtual Private Clouds and associated artifacts (routes, subnets, etc).
20
+ class VPC < MU::Cloud::VPC
21
+
22
+ # Subnets are almost a first-class resource. So let's kinda sorta treat
23
+ # them like one. This should only be invoked on objects that already
24
+ # exists in the cloud layer.
25
+ class Subnet < MU::Cloud::AWS::VPC
26
+
27
+ attr_reader :cloud_id
28
+ attr_reader :ip_block
29
+ attr_reader :mu_name
30
+ attr_reader :name
31
+ attr_reader :az
32
+ attr_reader :cloud_desc
33
+
34
+ # @param parent [MU::Cloud::AWS::VPC]: The parent VPC of this subnet.
35
+ # @param config [Hash<String>]:
36
+ def initialize(parent, config)
37
+ @parent = parent
38
+ @config = MU::Config.manxify(config)
39
+ @cloud_id = config['cloud_id']
40
+ @mu_name = config['mu_name']
41
+ @name = config['name']
42
+ @deploydata = config # This is a dummy for the sake of describe()
43
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_subnets(subnet_ids: [@cloud_id]).subnets.first
44
+ @az = resp.availability_zone
45
+ @ip_block = resp.cidr_block
46
+ @cloud_desc = resp # XXX this really isn't the cloud implementation's business
47
+
48
+ end
49
+
50
+ # Return the cloud identifier for the default route of this subnet.
51
+ # @return [String,nil]
52
+ def defaultRoute
53
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
54
+ filters: [{name: "association.subnet-id", values: [@cloud_id]}]
55
+ )
56
+ if resp.route_tables.size == 0 # use default route table for the VPC
57
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
58
+ filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
59
+ )
60
+ end
61
+ resp.route_tables.each { |route_table|
62
+ route_table.routes.each { |route|
63
+ if route.destination_cidr_block =="0.0.0.0/0" and route.state != "blackhole"
64
+ return route.instance_id if !route.instance_id.nil?
65
+ return route.gateway_id if !route.gateway_id.nil?
66
+ return route.vpc_peering_connection_id if !route.vpc_peering_connection_id.nil?
67
+ return route.network_interface_id if !route.network_interface_id.nil?
68
+ end
69
+ }
70
+ }
71
+ return nil
72
+ end
73
+
74
+ # Is this subnet privately-routable only, or public?
75
+ # @return [Boolean]
76
+ def private?
77
+ return false if @cloud_id.nil?
78
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
79
+ filters: [{name: "association.subnet-id", values: [@cloud_id]}]
80
+ )
81
+ if resp.route_tables.size == 0 # use default route table for the VPC
82
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
83
+ filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
84
+ )
85
+ end
86
+ resp.route_tables.each { |route_table|
87
+ route_table.routes.each { |route|
88
+ return false if !route.gateway_id.nil? and route.gateway_id != "local" # you can have an IgW and route it to a subset of IPs instead of 0.0.0.0/0
89
+ if route.destination_cidr_block == "0.0.0.0/0"
90
+ return true if !route.instance_id.nil?
91
+ return true if route.nat_gateway_id
92
+ end
93
+ }
94
+ }
95
+ return true
96
+ end
97
+ end # VPC::Subnet class
98
+
99
+ private
100
+
101
+ def create_subnets
102
+ return [] if @config['subnets'].nil? or @config['subnets'].empty?
103
+ nat_gateways = []
104
+
105
+ @eip_allocation_ids ||= []
106
+
107
+ subnetthreads = Array.new
108
+
109
+ azs = MU::Cloud::AWS.listAZs(region: @config['region'], credentials: @config['credentials'])
110
+ @config['subnets'].each { |subnet|
111
+ subnet_name = @config['name']+"-"+subnet['name']
112
+ az = subnet['availability_zone'] ? subnet['availability_zone'] : azs.op
113
+ MU.log "Creating Subnet #{subnet_name} (#{subnet['ip_block']}) in #{az}", details: subnet
114
+
115
+ subnetthreads << Thread.new {
116
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_subnet(
117
+ vpc_id: @cloud_id,
118
+ cidr_block: subnet['ip_block'],
119
+ availability_zone: az
120
+ ).subnet
121
+ subnet_id = subnet['subnet_id'] = resp.subnet_id
122
+
123
+ tag_me(subnet_id, @mu_name+"-"+subnet['name'])
124
+
125
+ loop_if = Proc.new {
126
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
127
+ (!resp or resp.state != "available")
128
+ }
129
+
130
+ MU.retrier([Aws::EC2::Errors::InvalidSubnetIDNotFound, NoMethodError], wait: 5, loop_if: loop_if) { |retries, _wait|
131
+ MU.log "Waiting for Subnet #{subnet_name} (#{subnet_id}) to become available", MU::NOTICE if retries > 0 and (retries % 3) == 0
132
+ }
133
+
134
+ if !subnet['route_table'].nil?
135
+ routes = {}
136
+ @config['route_tables'].each { |tbl|
137
+ routes[tbl['name']] = tbl
138
+ }
139
+ if routes[subnet['route_table']].nil?
140
+ raise "Subnet #{subnet_name} references nonexistent route #{subnet['route_table']}"
141
+ end
142
+ MU.log "Associating Route Table '#{subnet['route_table']}' (#{routes[subnet['route_table']]['route_table_id']}) with #{subnet_name}"
143
+ MU.retrier([Aws::EC2::Errors::InvalidRouteTableIDNotFound], wait: 10, max: 10) {
144
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).associate_route_table(
145
+ route_table_id: routes[subnet['route_table']]['route_table_id'],
146
+ subnet_id: subnet_id
147
+ )
148
+ }
149
+ end
150
+
151
+ if subnet.has_key?("map_public_ips")
152
+ MU.retrier([Aws::EC2::Errors::InvalidSubnetIDNotFound], wait: 10, max: 10) {
153
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_subnet_attribute(
154
+ subnet_id: subnet_id,
155
+ map_public_ip_on_launch: {
156
+ value: subnet['map_public_ips'],
157
+ }
158
+ )
159
+ }
160
+ end
161
+
162
+ if subnet['is_public'] and subnet['create_nat_gateway']
163
+ nat_gateways << create_nat_gateway(subnet)
164
+ end
165
+
166
+ if subnet["enable_traffic_logging"]
167
+ loggroup = @deploy.findLitterMate(name: @config['name']+"loggroup", type: "logs")
168
+ logrole = @deploy.findLitterMate(name: @config['name']+"logrole", type: "roles")
169
+ MU.log "Enabling traffic logging on Subnet #{subnet_name} in VPC #{@mu_name} to log group #{loggroup.mu_name}"
170
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_flow_logs(
171
+ resource_ids: [subnet_id],
172
+ resource_type: "Subnet",
173
+ traffic_type: subnet["traffic_type_to_log"],
174
+ log_group_name: loggroup.mu_name,
175
+ deliver_logs_permission_arn: logrole.cloudobj.arn
176
+ )
177
+ end
178
+ }
179
+ }
180
+
181
+ subnetthreads.each { |t|
182
+ t.join
183
+ }
184
+
185
+ nat_gateways
186
+ end
187
+
188
+ def allocate_eip_for_nat
189
+ MU::MommaCat.lock("nat-gateway-eipalloc")
190
+
191
+ eips = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_addresses(
192
+ filters: [
193
+ {
194
+ name: "domain",
195
+ values: ["vpc"]
196
+ }
197
+ ]
198
+ ).addresses
199
+
200
+ allocation_id = nil
201
+ eips.each { |eip|
202
+ next if !eip.association_id.nil? and !eip.association_id.empty?
203
+ if (eip.private_ip_address.nil? || eip.private_ip_address.empty?) and MU::MommaCat.lock(eip.allocation_id, true, true)
204
+ if !@eip_allocation_ids.include?(eip.allocation_id)
205
+ allocation_id = eip.allocation_id
206
+ break
207
+ end
208
+ end
209
+ }
210
+
211
+ if allocation_id.nil?
212
+ allocation_id = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).allocate_address(domain: "vpc").allocation_id
213
+ MU::MommaCat.lock(allocation_id, false, true)
214
+ end
215
+
216
+ @eip_allocation_ids << allocation_id
217
+
218
+ MU::MommaCat.unlock("nat-gateway-eipalloc")
219
+
220
+ allocation_id
221
+ end
222
+
223
+ def create_nat_gateway(subnet)
224
+ allocation_id = allocate_eip_for_nat
225
+
226
+ nat_gateway_id = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_nat_gateway(
227
+ subnet_id: subnet['subnet_id'],
228
+ allocation_id: allocation_id,
229
+ ).nat_gateway.nat_gateway_id
230
+
231
+ ensure_unlock = Proc.new { MU::MommaCat.unlock(allocation_id, true) }
232
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
233
+ loop_if = Proc.new {
234
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
235
+ resp.class != Aws::EC2::Types::NatGateway or resp.state == "pending"
236
+ }
237
+
238
+ MU.retrier([Aws::EmptyStructure, NoMethodError], wait: 5, max: 30, always: ensure_unlock, loop_if: loop_if) { |retries, _wait|
239
+ MU.log "Waiting for nat gateway #{nat_gateway_id} to become available (EIP allocation: #{allocation_id})" if retries % 5 == 0
240
+ }
241
+
242
+ raise MuError, "NAT Gateway failed #{nat_gateway_id}: #{resp}" if resp.state == "failed"
243
+
244
+ tag_me(nat_gateway_id)
245
+
246
+ {'id' => nat_gateway_id, 'availability_zone' => subnet['availability_zone']}
247
+ end
248
+
249
+ # Remove all subnets associated with the currently loaded deployment.
250
+ # @param noop [Boolean]: If true, will only print what would be done
251
+ # @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
252
+ # @param region [String]: The cloud provider region
253
+ # @return [void]
254
+ def self.purge_subnets(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion, credentials: nil)
255
+ resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_subnets(
256
+ filters: tagfilters
257
+ )
258
+ subnets = resp.data.subnets
259
+
260
+ return if subnets.nil? or subnets.size == 0
261
+
262
+ subnets.each { |subnet|
263
+ on_retry = Proc.new {
264
+ MU::Cloud::AWS::VPC.purge_interfaces(noop, [{name: "subnet-id", values: [subnet.subnet_id]}], region: region, credentials: credentials)
265
+ }
266
+
267
+ MU.log "Deleting Subnet #{subnet.subnet_id}"
268
+ MU.retrier([Aws::EC2::Errors::DependencyViolation], ignoreme: [Aws::EC2::Errors::InvalidSubnetIDNotFound], max: 20, on_retry: on_retry) { |_retries, wait|
269
+ begin
270
+ if subnet.state != "available"
271
+ MU.log "Waiting for #{subnet.subnet_id} to be in a removable state...", MU::NOTICE
272
+ sleep wait
273
+ else
274
+ MU::Cloud::AWS.ec2(credentials: credentials, region: region).delete_subnet(subnet_id: subnet.subnet_id) if !noop
275
+ end
276
+ end while subnet.state != "available"
277
+ }
278
+ }
279
+ end
280
+ private_class_method :purge_subnets
281
+
282
+ end # VPC class
283
+
284
+ end #class
285
+ end
286
+ end #module
@@ -47,6 +47,11 @@ module MU
47
47
  guid_chunks.join("-")
48
48
  end
49
49
 
50
+ # List all Azure subscriptions available to our credentials
51
+ def self.listHabitats(credentials = nil, use_cache: true)
52
+ []
53
+ end
54
+
50
55
  # A hook that is always called just before any of the instance method of
51
56
  # our resource implementations gets invoked, so that we can ensure that
52
57
  # repetitive setup tasks (like resolving +:resource_group+ for Azure
@@ -77,6 +82,11 @@ module MU
77
82
  [:resource_group]
78
83
  end
79
84
 
85
+ # Is this a "real" cloud provider, or a stub like CloudFormation?
86
+ def self.virtual?
87
+ false
88
+ end
89
+
80
90
  # Stub class to represent Azure's resource identifiers, which look like:
81
91
  # /subscriptions/3d20ddd8-4652-4074-adda-0d127ef1f0e0/resourceGroups/mu/providers/Microsoft.Network/virtualNetworks/mu-vnet
82
92
  # Various API calls need chunks of this in different contexts, and this
@@ -274,6 +284,9 @@ module MU
274
284
  end
275
285
  raise e
276
286
  end
287
+ if !sdk_response
288
+ raise MuError, "Nil response from Azure API attempting list_locations(#{subscription})"
289
+ end
277
290
 
278
291
  sdk_response.value.each do | region |
279
292
  @@regions.push(region.name)
@@ -218,11 +218,7 @@ module MU
218
218
  "Azure Kubernetes Service Cluster Admin Role"
219
219
  ]
220
220
  }
221
- cluster['dependencies'] ||= []
222
- cluster['dependencies'] << {
223
- "type" => "user",
224
- "name" => cluster["name"]+"user"
225
- }
221
+ MU::Config.addDependency(cluster, cluster['name']+"user", "user")
226
222
 
227
223
  ok = false if !configurator.insertKitten(svcacct_desc, "users")
228
224
 
@@ -337,7 +337,14 @@ module MU
337
337
  # We assume that any values we have in +@config+ are placeholders, and
338
338
  # calculate our own accordingly based on what's live in the cloud.
339
339
  def toKitten(**args)
340
- bok = {}
340
+
341
+ bok = {
342
+ "cloud" => "Azure",
343
+ "name" => cloud_desc.name,
344
+ "project" => @config['project'],
345
+ "credentials" => @config['credentials'],
346
+ "cloud_id" => @cloud_id.to_s
347
+ }
341
348
 
342
349
  bok
343
350
  end
@@ -146,7 +146,7 @@ module MU
146
146
  return nil if @config.nil? or @deploy.nil?
147
147
 
148
148
  nat_ssh_key = nat_ssh_user = nat_ssh_host = nil
149
- if !@config["vpc"].nil? and !MU::Cloud::Azure::VPC.haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
149
+ if !@config["vpc"].nil? and !MU::Cloud.resourceClass("Azure", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
150
150
 
151
151
  if !@nat.nil? and @nat.mu_name != @mu_name
152
152
  if @nat.cloud_desc.nil?
@@ -189,7 +189,7 @@ module MU
189
189
  end
190
190
 
191
191
  _nat_ssh_key, _nat_ssh_user, nat_ssh_host, _canonical_ip, _ssh_user, _ssh_key_name = getSSHConfig
192
- if !nat_ssh_host and !MU::Cloud::Azure::VPC.haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
192
+ if !nat_ssh_host and !MU::Cloud.resourceClass("Azure", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
193
193
  # XXX check if canonical_ip is in the private ranges
194
194
  # raise MuError, "#{node} has no NAT host configured, and I have no other route to it"
195
195
  end
@@ -384,7 +384,7 @@ module MU
384
384
  # Our deploydata gets corrupted often with server pools, this will cause us to use the wrong IP to identify a node
385
385
  # which will cause us to create certificates, DNS records and other artifacts with incorrect information which will cause our deploy to fail.
386
386
  # The cloud_id is always correct so lets use 'cloud_desc' to get the correct IPs
387
- if MU::Cloud::Azure::VPC.haveRouteToInstance?(cloud_desc, credentials: @config['credentials']) or public_ips.size == 0
387
+ if MU::Cloud.resourceClass("Azure", "VPC").haveRouteToInstance?(cloud_desc, credentials: @config['credentials']) or public_ips.size == 0
388
388
  @config['canonical_ip'] = private_ips.first
389
389
  return private_ips.first
390
390
  else
@@ -393,6 +393,28 @@ module MU
393
393
  end
394
394
  end
395
395
 
396
+ # Return all of the IP addresses, public and private, from all of our
397
+ # network interfaces.
398
+ # @return [Array<String>]
399
+ def listIPs
400
+ ips = []
401
+ cloud_desc.network_profile.network_interfaces.each { |iface|
402
+ iface_id = Id.new(iface.is_a?(Hash) ? iface['id'] : iface.id)
403
+ iface_desc = MU::Cloud::Azure.network(credentials: @credentials).network_interfaces.get(@resource_group, iface_id.to_s)
404
+ iface_desc.ip_configurations.each { |ipcfg|
405
+ ips << ipcfg.private_ipaddress
406
+ if ipcfg.respond_to?(:public_ipaddress) and ipcfg.public_ipaddress
407
+ ip_id = Id.new(ipcfg.public_ipaddress.id)
408
+ ip_desc = MU::Cloud::Azure.network(credentials: @credentials).public_ipaddresses.get(@resource_group, ip_id.to_s)
409
+ if ip_desc
410
+ ips << ip_desc.ip_address
411
+ end
412
+ end
413
+ }
414
+ }
415
+ ips
416
+ end
417
+
396
418
  # return [String]: A password string.
397
419
  def getWindowsAdminPassword
398
420
  end
@@ -430,7 +452,7 @@ module MU
430
452
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
431
453
  # @param region [String]: The cloud provider region
432
454
  # @return [void]
433
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
455
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
434
456
  end
435
457
 
436
458
  # Cloud-specific configuration properties.
@@ -441,7 +463,7 @@ module MU
441
463
  hosts_schema = MU::Config::CIDR_PRIMITIVE
442
464
  hosts_schema["pattern"] = "^(\\d+\\.\\d+\\.\\d+\\.\\d+\/[0-9]{1,2}|\\*)$"
443
465
  schema = {
444
- "roles" => MU::Cloud::Azure::User.schema(config)[1]["roles"],
466
+ "roles" => MU::Cloud.resourceClass("Azure", "User").schema(config)[1]["roles"],
445
467
  "ingress_rules" => {
446
468
  "items" => {
447
469
  "properties" => {
@@ -497,8 +519,7 @@ module MU
497
519
  foundmatch = false
498
520
  MU::Cloud.availableClouds.each { |cloud|
499
521
  next if cloud == "Azure"
500
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
501
- foreign_types = (cloudbase.listInstanceTypes).values.first
522
+ foreign_types = (MU::Cloud.cloudClass(cloud).listInstanceTypes).values.first
502
523
  if foreign_types.size == 1
503
524
  foreign_types = foreign_types.values.first
504
525
  end
@@ -590,18 +611,8 @@ module MU
590
611
  if !configurator.insertKitten(vpc, "vpcs", true)
591
612
  ok = false
592
613
  end
593
- server['dependencies'] ||= []
594
-
595
- server['dependencies'] << {
596
- "type" => "vpc",
597
- "name" => server['name']+"vpc"
598
- }
599
- # XXX what happens if there's no natstion here?
600
- server['dependencies'] << {
601
- "type" => "server",
602
- "name" => server['name']+"vpc-natstion",
603
- "phase" => "groom"
604
- }
614
+ MU::Config.addDependency(server, server['name']+"vpc", "vpc")
615
+ MU::Config.addDependency(server, server['name']+"vpc-natstion", "server", phase: "groom")
605
616
  server['vpc'] = {
606
617
  "name" => server['name']+"vpc",
607
618
  "subnet_pref" => "private"
@@ -618,17 +629,14 @@ module MU
618
629
  "credentials" => server["credentials"],
619
630
  "roles" => server["roles"]
620
631
  }
621
- server['dependencies'] ||= []
622
- server['dependencies'] << {
623
- "type" => "user",
624
- "name" => server["name"]+"user"
625
- }
632
+ MU::Config.addDependency(server, server['name']+"user", "user")
626
633
 
627
634
  ok = false if !configurator.insertKitten(svcacct_desc, "users")
628
635
 
629
636
  ok
630
637
  end
631
638
 
639
+ # stub
632
640
  def self.diskConfig(config, create = true, disk_as_url = true, credentials: nil)
633
641
  end
634
642