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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  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 +21 -13
  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 +4 -4
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +147 -37
  23. data/cloud-mu.gemspec +22 -20
  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 +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +158 -111
  45. data/modules/mu/adoption.rb +404 -71
  46. data/modules/mu/cleanup.rb +221 -306
  47. data/modules/mu/cloud.rb +129 -1633
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +44 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +926 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +169 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +32 -3
  61. data/modules/mu/config/cache_cluster.rb +2 -2
  62. data/modules/mu/config/cdn.rb +100 -0
  63. data/modules/mu/config/collection.rb +4 -4
  64. data/modules/mu/config/container_cluster.rb +9 -4
  65. data/modules/mu/config/database.rb +84 -105
  66. data/modules/mu/config/database.yml +1 -2
  67. data/modules/mu/config/dnszone.rb +10 -9
  68. data/modules/mu/config/doc_helpers.rb +516 -0
  69. data/modules/mu/config/endpoint.rb +5 -4
  70. data/modules/mu/config/firewall_rule.rb +103 -4
  71. data/modules/mu/config/folder.rb +4 -4
  72. data/modules/mu/config/function.rb +19 -10
  73. data/modules/mu/config/group.rb +4 -4
  74. data/modules/mu/config/habitat.rb +4 -4
  75. data/modules/mu/config/job.rb +89 -0
  76. data/modules/mu/config/loadbalancer.rb +60 -14
  77. data/modules/mu/config/log.rb +4 -4
  78. data/modules/mu/config/msg_queue.rb +4 -4
  79. data/modules/mu/config/nosqldb.rb +4 -4
  80. data/modules/mu/config/notifier.rb +10 -21
  81. data/modules/mu/config/ref.rb +411 -0
  82. data/modules/mu/config/role.rb +4 -4
  83. data/modules/mu/config/schema_helpers.rb +509 -0
  84. data/modules/mu/config/search_domain.rb +4 -4
  85. data/modules/mu/config/server.rb +98 -71
  86. data/modules/mu/config/server.yml +1 -0
  87. data/modules/mu/config/server_pool.rb +5 -9
  88. data/modules/mu/config/storage_pool.rb +1 -1
  89. data/modules/mu/config/tail.rb +200 -0
  90. data/modules/mu/config/user.rb +4 -4
  91. data/modules/mu/config/vpc.rb +71 -27
  92. data/modules/mu/config/vpc.yml +0 -1
  93. data/modules/mu/defaults/AWS.yaml +91 -68
  94. data/modules/mu/defaults/Azure.yaml +1 -0
  95. data/modules/mu/defaults/Google.yaml +3 -2
  96. data/modules/mu/deploy.rb +43 -26
  97. data/modules/mu/groomer.rb +17 -2
  98. data/modules/mu/groomers/ansible.rb +188 -41
  99. data/modules/mu/groomers/chef.rb +116 -55
  100. data/modules/mu/logger.rb +127 -148
  101. data/modules/mu/master.rb +410 -2
  102. data/modules/mu/master/chef.rb +3 -4
  103. data/modules/mu/master/ldap.rb +3 -3
  104. data/modules/mu/master/ssl.rb +12 -3
  105. data/modules/mu/mommacat.rb +218 -2612
  106. data/modules/mu/mommacat/daemon.rb +403 -0
  107. data/modules/mu/mommacat/naming.rb +473 -0
  108. data/modules/mu/mommacat/search.rb +495 -0
  109. data/modules/mu/mommacat/storage.rb +722 -0
  110. data/modules/mu/{clouds → providers}/README.md +1 -1
  111. data/modules/mu/{clouds → providers}/aws.rb +380 -122
  112. data/modules/mu/{clouds → providers}/aws/alarm.rb +7 -5
  113. data/modules/mu/{clouds → providers}/aws/bucket.rb +297 -59
  114. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +37 -71
  115. data/modules/mu/providers/aws/cdn.rb +782 -0
  116. data/modules/mu/{clouds → providers}/aws/collection.rb +26 -25
  117. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +724 -744
  118. data/modules/mu/providers/aws/database.rb +1744 -0
  119. data/modules/mu/{clouds → providers}/aws/dnszone.rb +88 -70
  120. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  121. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +220 -247
  122. data/modules/mu/{clouds → providers}/aws/folder.rb +8 -8
  123. data/modules/mu/{clouds → providers}/aws/function.rb +300 -142
  124. data/modules/mu/{clouds → providers}/aws/group.rb +31 -29
  125. data/modules/mu/{clouds → providers}/aws/habitat.rb +18 -15
  126. data/modules/mu/providers/aws/job.rb +466 -0
  127. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +66 -56
  128. data/modules/mu/{clouds → providers}/aws/log.rb +17 -14
  129. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +29 -19
  130. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +114 -16
  131. data/modules/mu/{clouds → providers}/aws/notifier.rb +142 -65
  132. data/modules/mu/{clouds → providers}/aws/role.rb +158 -118
  133. data/modules/mu/{clouds → providers}/aws/search_domain.rb +201 -59
  134. data/modules/mu/{clouds → providers}/aws/server.rb +844 -1139
  135. data/modules/mu/{clouds → providers}/aws/server_pool.rb +74 -65
  136. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +26 -44
  137. data/modules/mu/{clouds → providers}/aws/user.rb +24 -25
  138. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  139. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  140. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  141. data/modules/mu/{clouds → providers}/aws/vpc.rb +525 -931
  142. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  143. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  144. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  145. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  146. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  147. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  148. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  149. data/modules/mu/{clouds → providers}/azure/server.rb +97 -49
  150. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  151. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  152. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  153. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  154. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  155. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  156. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  160. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  161. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  162. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  163. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  164. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  165. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  166. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  167. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  168. data/modules/mu/{clouds → providers}/google.rb +68 -30
  169. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  170. data/modules/mu/{clouds → providers}/google/container_cluster.rb +85 -78
  171. data/modules/mu/{clouds → providers}/google/database.rb +11 -21
  172. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  173. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  174. data/modules/mu/{clouds → providers}/google/function.rb +140 -168
  175. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  176. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  177. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +19 -21
  178. data/modules/mu/{clouds → providers}/google/role.rb +94 -58
  179. data/modules/mu/{clouds → providers}/google/server.rb +243 -156
  180. data/modules/mu/{clouds → providers}/google/server_pool.rb +26 -45
  181. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  182. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  183. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  184. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  185. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  186. data/modules/tests/aws-jobs-functions.yaml +46 -0
  187. data/modules/tests/bucket.yml +4 -0
  188. data/modules/tests/centos6.yaml +15 -0
  189. data/modules/tests/centos7.yaml +15 -0
  190. data/modules/tests/centos8.yaml +12 -0
  191. data/modules/tests/ecs.yaml +23 -0
  192. data/modules/tests/eks.yaml +1 -1
  193. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  194. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  195. data/modules/tests/includes-and-params.yaml +2 -1
  196. data/modules/tests/microservice_app.yaml +288 -0
  197. data/modules/tests/rds.yaml +108 -0
  198. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  199. data/modules/tests/regrooms/bucket.yml +19 -0
  200. data/modules/tests/regrooms/rds.yaml +123 -0
  201. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  202. data/modules/tests/super_complex_bok.yml +2 -2
  203. data/modules/tests/super_simple_bok.yml +3 -5
  204. data/modules/tests/win2k12.yaml +17 -5
  205. data/modules/tests/win2k16.yaml +25 -0
  206. data/modules/tests/win2k19.yaml +25 -0
  207. data/requirements.txt +1 -0
  208. data/spec/mu/clouds/azure_spec.rb +2 -2
  209. metadata +240 -154
  210. data/extras/image-generators/AWS/windows.yaml +0 -18
  211. data/modules/mu/clouds/aws/database.rb +0 -1985
  212. data/modules/mu/clouds/aws/endpoint.rb +0 -592
@@ -59,8 +59,10 @@ module MU
59
59
  def groom
60
60
  end
61
61
 
62
+ @cached_cloud_desc = nil
62
63
  # Return the cloud descriptor for the Habitat
63
- def cloud_desc
64
+ def cloud_desc(use_cache: true)
65
+ return @cached_cloud_desc if @cached_cloud_desc and use_cache
64
66
  @cached_cloud_desc ||= MU::Cloud::Azure::Habitat.find(cloud_id: @cloud_id).values.first
65
67
  # @habitat_id ||= @cached_cloud_desc.parent.id if @cached_cloud_desc
66
68
  @cached_cloud_desc
@@ -131,7 +133,7 @@ module MU
131
133
  # Reverse-map our cloud description into a runnable config hash.
132
134
  # We assume that any values we have in +@config+ are placeholders, and
133
135
  # calculate our own accordingly based on what's live in the cloud.
134
- def toKitten(rootparent: nil, billing: nil)
136
+ def toKitten(**args)
135
137
  bok = {
136
138
  "cloud" => "Azure",
137
139
  "credentials" => @config['credentials']
@@ -141,9 +143,9 @@ module MU
141
143
  end
142
144
 
143
145
  # Cloud-specific configuration properties.
144
- # @param config [MU::Config]: The calling MU::Config object
146
+ # @param _config [MU::Config]: The calling MU::Config object
145
147
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
146
- def self.schema(config)
148
+ def self.schema(_config)
147
149
  toplevel_required = []
148
150
  schema = {
149
151
  }
@@ -152,9 +154,9 @@ module MU
152
154
 
153
155
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::habitats}, bare and unvalidated.
154
156
  # @param habitat [Hash]: The resource to process and validate
155
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
157
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
156
158
  # @return [Boolean]: True if validation succeeded, False otherwise
157
- def self.validateConfig(habitat, configurator)
159
+ def self.validateConfig(habitat, _configurator)
158
160
  ok = true
159
161
  habitat['region'] ||= MU::Cloud::Azure.myRegion(habitat['credentials'])
160
162
 
@@ -74,9 +74,9 @@ module MU
74
74
  end
75
75
 
76
76
  # Cloud-specific configuration properties.
77
- # @param config [MU::Config]: The calling MU::Config object
77
+ # @param _config [MU::Config]: The calling MU::Config object
78
78
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
79
- def self.schema(config)
79
+ def self.schema(_config)
80
80
  toplevel_required = []
81
81
  schema = {
82
82
  # "named_ports" => {
@@ -102,9 +102,9 @@ module MU
102
102
 
103
103
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::loadbalancers}, bare and unvalidated.
104
104
  # @param lb [Hash]: The resource to process and validate
105
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
105
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
106
106
  # @return [Boolean]: True if validation succeeded, False otherwise
107
- def self.validateConfig(lb, configurator)
107
+ def self.validateConfig(lb, _configurator)
108
108
  ok = true
109
109
  lb['region'] ||= MU::Cloud::Azure.myRegion(lb['credentials'])
110
110
 
@@ -144,7 +144,7 @@ module MU
144
144
  found[Id.new(lb.id)] = lb
145
145
  }
146
146
  else
147
- MU::Cloud::Azure.network(credentials: args[:credentials]).load_balancers.list_all.each { |net|
147
+ MU::Cloud::Azure.network(credentials: args[:credentials]).load_balancers.list_all.each { |lb|
148
148
  found[Id.new(lb.id)] = lb
149
149
  }
150
150
  end
@@ -65,8 +65,8 @@ module MU
65
65
  end
66
66
 
67
67
  # Assign this role object to a given principal (create a RoleAssignment)
68
- # @param principal [MU::Cloud::Azure::Id]
69
- def assignTo(principal)
68
+ # @param principal_id [MU::Cloud::Azure::Id]
69
+ def assignTo(principal_id)
70
70
  MU::Cloud::Azure::Role.assignTo(principal_id, role_id: @cloud_id)
71
71
  end
72
72
 
@@ -145,7 +145,7 @@ module MU
145
145
  begin
146
146
  resp = MU::Cloud::Azure.authorization(credentials: args[:credentials]).role_definitions.get(scope, id_str)
147
147
  found[Id.new(resp.id)] = resp
148
- rescue MsRestAzure::AzureOperationError => e
148
+ rescue MsRestAzure::AzureOperationError
149
149
  # this is fine, we're doing a blind search after all
150
150
  end
151
151
  else
@@ -155,7 +155,7 @@ module MU
155
155
  end
156
156
  }
157
157
  if args[:role_name]
158
- @@role_list_cache[scope].each_pair { |key, role|
158
+ @@role_list_cache[scope].values.each { |role|
159
159
  begin
160
160
  if role.role_name == args[:role_name]
161
161
  found[Id.new(role.id)] = role
@@ -183,9 +183,9 @@ module MU
183
183
  end
184
184
 
185
185
  # Cloud-specific configuration properties.
186
- # @param config [MU::Config]: The calling MU::Config object
186
+ # @param _config [MU::Config]: The calling MU::Config object
187
187
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
188
- def self.schema(config)
188
+ def self.schema(_config)
189
189
  toplevel_required = []
190
190
  schema = {
191
191
  }
@@ -194,17 +194,15 @@ module MU
194
194
 
195
195
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::roles}, bare and unvalidated.
196
196
  # @param role [Hash]: The resource to process and validate
197
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
197
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
198
198
  # @return [Boolean]: True if validation succeeded, False otherwise
199
- def self.validateConfig(role, configurator)
199
+ def self.validateConfig(role, _configurator)
200
200
  ok = true
201
201
  role['region'] ||= MU::Cloud::Azure.myRegion(role['credentials'])
202
202
 
203
203
  ok
204
204
  end
205
205
 
206
- private
207
-
208
206
  end
209
207
  end
210
208
  end
@@ -139,21 +139,21 @@ module MU
139
139
  # Figure out what's needed to SSH into this server.
140
140
  # @return [Array<String>]: nat_ssh_key, nat_ssh_user, nat_ssh_host, canonical_ip, ssh_user, ssh_key_name, alternate_names
141
141
  def getSSHConfig
142
- node, config, deploydata = describe(cloud_id: @cloud_id)
142
+ describe(cloud_id: @cloud_id)
143
143
  # XXX add some awesome alternate names from metadata and make sure they end
144
144
  # up in MU::MommaCat's ssh config wangling
145
145
  ssh_keydir = Etc.getpwuid(Process.uid).dir+"/.ssh"
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?
153
153
  MU.log "NAT was missing cloud descriptor when called in #{@mu_name}'s getSSHConfig", MU::ERR
154
154
  return nil
155
155
  end
156
- foo, bar, baz, nat_ssh_host, nat_ssh_user, nat_ssh_key = @nat.getSSHConfig
156
+ _foo, _bar, _baz, nat_ssh_host, nat_ssh_user, nat_ssh_key = @nat.getSSHConfig
157
157
  if nat_ssh_user.nil? and !nat_ssh_host.nil?
158
158
  MU.log "#{@config["name"]} (#{MU.deploy_id}) is configured to use #{@config['vpc']} NAT #{nat_ssh_host}, but username isn't specified. Guessing root.", MU::ERR, details: caller
159
159
  nat_ssh_user = "root"
@@ -163,7 +163,7 @@ module MU
163
163
 
164
164
  if @config['ssh_user'].nil?
165
165
  if windows?
166
- @config['ssh_user'] = "Administrator"
166
+ @config['ssh_user'] = "muadmin"
167
167
  else
168
168
  @config['ssh_user'] = "root"
169
169
  end
@@ -188,8 +188,8 @@ module MU
188
188
  @named = true
189
189
  end
190
190
 
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'])
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.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
@@ -236,7 +236,7 @@ module MU
236
236
  resp = MU::Cloud::Azure.compute(credentials: args[:credentials]).virtual_machines.get(rg, id_str)
237
237
  next if resp.nil?
238
238
  found[Id.new(resp.id)] = resp
239
- rescue MU::Cloud::Azure::APIError => e
239
+ rescue MU::Cloud::Azure::APIError
240
240
  # this is fine, we're doing a blind search after all
241
241
  end
242
242
  }
@@ -267,7 +267,7 @@ module MU
267
267
 
268
268
  MU::MommaCat.lock(@cloud_id.to_s+"-groom")
269
269
 
270
- node, config, deploydata = describe(cloud_id: @cloud_id)
270
+ node, _config, deploydata = describe(cloud_id: @cloud_id)
271
271
 
272
272
  if node.nil? or node.empty?
273
273
  raise MuError, "MU::Cloud::Azure::Server.groom was called without a mu_name"
@@ -357,7 +357,7 @@ module MU
357
357
  # bastion hosts that may be in the path, see getSSHConfig if that's what
358
358
  # you need.
359
359
  def canonicalIP
360
- mu_name, config, deploydata = describe(cloud_id: @cloud_id)
360
+ describe(cloud_id: @cloud_id)
361
361
 
362
362
  if !cloud_desc
363
363
  raise MuError, "Couldn't retrieve cloud descriptor for server #{self}"
@@ -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" => {
@@ -451,6 +473,34 @@ module MU
451
473
  }
452
474
  }
453
475
  }
476
+ },
477
+ "windows_admin_username" => {
478
+ "type" => "string",
479
+ "default" => "muadmin",
480
+ },
481
+ "ssh_user" => {
482
+ "default_if" => [
483
+ {
484
+ "key_is" => "platform",
485
+ "value_is" => "windows",
486
+ "set" => "muadmin"
487
+ },
488
+ {
489
+ "key_is" => "platform",
490
+ "value_is" => "win2k12",
491
+ "set" => "muadmin"
492
+ },
493
+ {
494
+ "key_is" => "platform",
495
+ "value_is" => "win2k12r2",
496
+ "set" => "muadmin"
497
+ },
498
+ {
499
+ "key_is" => "platform",
500
+ "value_is" => "win2k16",
501
+ "set" => "muadmin"
502
+ }
503
+ ]
454
504
  }
455
505
  }
456
506
  [toplevel_required, schema]
@@ -469,8 +519,7 @@ module MU
469
519
  foundmatch = false
470
520
  MU::Cloud.availableClouds.each { |cloud|
471
521
  next if cloud == "Azure"
472
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
473
- foreign_types = (cloudbase.listInstanceTypes).values.first
522
+ foreign_types = (MU::Cloud.cloudClass(cloud).listInstanceTypes).values.first
474
523
  if foreign_types.size == 1
475
524
  foreign_types = foreign_types.values.first
476
525
  end
@@ -479,6 +528,7 @@ module MU
479
528
  mem = foreign_types[size]["memory"]
480
529
  ecu = foreign_types[size]["ecu"]
481
530
  types.keys.sort.reverse.each { |type|
531
+ next if type.match(/_Promo$/i)
482
532
  features = types[type]
483
533
  next if ecu == "Variable" and ecu != features["ecu"]
484
534
  next if features["vcpu"] != vcpu
@@ -495,7 +545,6 @@ module MU
495
545
 
496
546
  if !foundmatch
497
547
  MU.log "Invalid size '#{size}' for Azure Compute instance in #{region}. Supported types:", MU::ERR, details: types.keys.sort.join(", ")
498
- exit
499
548
  return nil
500
549
  end
501
550
  end
@@ -512,6 +561,10 @@ exit
512
561
 
513
562
  server['region'] ||= MU::Cloud::Azure.myRegion(server['credentials'])
514
563
  server['ssh_user'] ||= "muadmin"
564
+ if server['windows_admin_username'] == "Administrator"
565
+ MU.log "Azure does not permit admin user to be 'Administrator'", MU::ERR
566
+ ok = false
567
+ end
515
568
 
516
569
  server['size'] = validateInstanceType(server["size"], server["region"])
517
570
  ok = false if server['size'].nil?
@@ -527,17 +580,17 @@ exit
527
580
  end
528
581
 
529
582
  image_desc = MU::Cloud::Azure::Server.fetchImage(server['image_id'].to_s, credentials: server['credentials'], region: server['region'])
530
- if image_desc.plan
531
- terms = MU::Cloud::Azure.marketplace(credentials: @credentials).marketplace_agreements.get(image_desc.plan.publisher, image_desc.plan.product, image_desc.plan.name)
532
- if !terms.accepted
533
- MU.log "Deploying #{server['name']} will automatically agree to the licensing terms for #{terms.product}", MU::NOTICE, details: terms.license_text_link
534
- end
535
- end
536
583
 
537
584
  if !image_desc
538
585
  MU.log "Failed to locate an Azure VM image for #{server['name']} from #{server['image_id']} in #{server['region']}", MU::ERR
539
586
  ok = false
540
587
  else
588
+ if image_desc.plan
589
+ terms = MU::Cloud::Azure.marketplace(credentials: @credentials).marketplace_agreements.get(image_desc.plan.publisher, image_desc.plan.product, image_desc.plan.name)
590
+ if !terms.accepted
591
+ MU.log "Deploying #{server['name']} will automatically agree to the licensing terms for #{terms.product}", MU::NOTICE, details: terms.license_text_link
592
+ end
593
+ end
541
594
  server['image_id'] = image_desc.id
542
595
  end
543
596
 
@@ -558,18 +611,8 @@ exit
558
611
  if !configurator.insertKitten(vpc, "vpcs", true)
559
612
  ok = false
560
613
  end
561
- server['dependencies'] ||= []
562
-
563
- server['dependencies'] << {
564
- "type" => "vpc",
565
- "name" => server['name']+"vpc"
566
- }
567
- # XXX what happens if there's no natstion here?
568
- server['dependencies'] << {
569
- "type" => "server",
570
- "name" => server['name']+"vpc-natstion",
571
- "phase" => "groom"
572
- }
614
+ MU::Config.addDependency(server, server['name']+"vpc", "vpc")
615
+ MU::Config.addDependency(server, server['name']+"vpc-natstion", "server", phase: "groom")
573
616
  server['vpc'] = {
574
617
  "name" => server['name']+"vpc",
575
618
  "subnet_pref" => "private"
@@ -586,17 +629,14 @@ exit
586
629
  "credentials" => server["credentials"],
587
630
  "roles" => server["roles"]
588
631
  }
589
- server['dependencies'] ||= []
590
- server['dependencies'] << {
591
- "type" => "user",
592
- "name" => server["name"]+"user"
593
- }
632
+ MU::Config.addDependency(server, server['name']+"user", "user")
594
633
 
595
634
  ok = false if !configurator.insertKitten(svcacct_desc, "users")
596
635
 
597
636
  ok
598
637
  end
599
638
 
639
+ # stub
600
640
  def self.diskConfig(config, create = true, disk_as_url = true, credentials: nil)
601
641
  end
602
642
 
@@ -624,17 +664,22 @@ exit
624
664
  skus = MU::Cloud::Azure.compute(credentials: credentials).virtual_machine_images.list_skus(region, publisher, offer).map { |s| s.name }
625
665
 
626
666
  if !skus.include?(sku)
627
- skus.sort { |a, b| MU.version_sort(a, b) }.reverse.each { |s|
628
- if s.match(/^#{Regexp.quote(sku)}/)
629
- sku = s
630
- break
631
- end
632
- }
667
+ skus.reject! { |s| !s.match(/^#{Regexp.quote(sku)}/) }
668
+ skus.sort! { |a, b| MU.version_sort(a, b) }.reverse!
669
+ sku = skus.first
633
670
  end
634
671
 
635
- versions = MU::Cloud::Azure.compute(credentials: credentials).virtual_machine_images.list(region, publisher, offer, sku).map { |v| v.name }
672
+ version = nil
673
+ begin
674
+ versions = MU::Cloud::Azure.compute(credentials: credentials).virtual_machine_images.list(region, publisher, offer, sku).map { |v| v.name }
675
+ if versions.nil? or versions.empty?
676
+ skus.delete(sku)
677
+ sku = skus.first
678
+ end
679
+ end while skus.size > 0 and (versions.nil? or versions.empty?)
680
+
636
681
  if versions.nil? or versions.empty?
637
- MU.log "Azure API returned empty machine image version list for publisher #{publisher} offer #{offer} sku #{sku}", MU::ERR
682
+ MU.log "Azure API returned empty machine image version list for publisher #{publisher} offer #{offer} sku #{sku}", MU::ERR, details: skus
638
683
  return nil
639
684
  end
640
685
 
@@ -724,12 +769,15 @@ exit
724
769
  hw_obj.vm_size = @config['size']
725
770
 
726
771
  os_obj = MU::Cloud::Azure.compute(:OSProfile).new
727
- os_obj.admin_username = @config['ssh_user']
728
- os_obj.computer_name = @mu_name
729
772
  if windows?
730
773
  win_obj = MU::Cloud::Azure.compute(:WindowsConfiguration).new
731
774
  os_obj.windows_configuration = win_obj
775
+ os_obj.admin_username = @config['windows_admin_username']
776
+ os_obj.admin_password = MU.generateWindowsPassword
777
+ os_obj.computer_name = @deploy.getResourceName(@config["name"], max_length: 15, disallowed_chars: /[~!@#$%^&*()=+_\[\]{}\\\|;:\.'",<>\/\?]/)
732
778
  else
779
+ os_obj.admin_username = @config['ssh_user']
780
+ os_obj.computer_name = @mu_name
733
781
  key_obj = MU::Cloud::Azure.compute(:SshPublicKey).new
734
782
  key_obj.key_data = @deploy.ssh_public_key
735
783
  key_obj.path = "/home/#{@config['ssh_user']}/.ssh/authorized_keys"
@@ -772,7 +820,7 @@ exit
772
820
  begin
773
821
  # XXX this doesn't actually work as documented
774
822
  MU::Cloud::Azure.marketplace(credentials: @credentials).marketplace_agreements.sign(image_desc.plan.publisher, image_desc.plan.product, image_desc.plan.name)
775
- rescue Exception => e
823
+ rescue StandardError => e
776
824
  MU.log e.message, MU::ERR
777
825
  vm_obj.plan = nil
778
826
  end
@@ -86,7 +86,6 @@ module MU
86
86
 
87
87
  client = ::MsRest::ServiceClient.new(cred_obj)
88
88
  cloud_desc.client_secret_url.match(/^(http.*?\.azure\.net)(\/.*)/)
89
- base = Regexp.last_match[1]
90
89
  path = Regexp.last_match[2]
91
90
  #MU.log "Calling into #{base} #{path}"
92
91
  promise = client.make_request_async(
@@ -97,7 +96,7 @@ module MU
97
96
 
98
97
  # XXX this is async, need to stop and wait somehow
99
98
  promise.then do | result|
100
- resp = result.response
99
+ result.response
101
100
  # MU.log "RESPONSE", MU::WARN, details: resp
102
101
  end
103
102
  end
@@ -106,10 +105,9 @@ module MU
106
105
 
107
106
  # Called automatically by {MU::Deploy#createResources}
108
107
  def groom
109
- rgroup_name = @deploy.deploy_id+"-"+@config['region'].upcase
110
108
  if @config['roles']
111
109
  @config['roles'].each { |role|
112
- MU::Cloud::Azure::Role.assignTo(cloud_desc.principal_id, role_name: role, credentials: @config['credentials'])
110
+ MU::Cloud.resourceClass("Azure", "Role").assignTo(cloud_desc.principal_id, role_name: role, credentials: @config['credentials'])
113
111
  }
114
112
  end
115
113
  end
@@ -191,9 +189,9 @@ module MU
191
189
  end
192
190
 
193
191
  # Cloud-specific configuration properties.
194
- # @param config [MU::Config]: The calling MU::Config object
192
+ # @param _config [MU::Config]: The calling MU::Config object
195
193
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
196
- def self.schema(config)
194
+ def self.schema(_config)
197
195
  toplevel_required = []
198
196
  schema = {
199
197
  "region" => MU::Config.region_primitive,
@@ -221,9 +219,9 @@ module MU
221
219
 
222
220
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::users}, bare and unvalidated.
223
221
  # @param user [Hash]: The resource to process and validate
224
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
222
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
225
223
  # @return [Boolean]: True if validation succeeded, False otherwise
226
- def self.validateConfig(user, configurator)
224
+ def self.validateConfig(user, _configurator)
227
225
  ok = true
228
226
  user['region'] ||= MU::Cloud::Azure.myRegion(user['credentials'])
229
227