cloud-mu 3.1.6 → 3.4.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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/bin/mu-adopt +15 -12
  4. data/bin/mu-azure-tests +57 -0
  5. data/bin/mu-cleanup +2 -4
  6. data/bin/mu-configure +37 -1
  7. data/bin/mu-deploy +3 -3
  8. data/bin/mu-findstray-tests +25 -0
  9. data/bin/mu-gen-docs +2 -4
  10. data/bin/mu-load-config.rb +2 -1
  11. data/bin/mu-run-tests +37 -12
  12. data/cloud-mu.gemspec +4 -4
  13. data/cookbooks/mu-tools/attributes/default.rb +7 -0
  14. data/cookbooks/mu-tools/libraries/helper.rb +87 -3
  15. data/cookbooks/mu-tools/recipes/apply_security.rb +39 -23
  16. data/cookbooks/mu-tools/recipes/aws_api.rb +13 -0
  17. data/cookbooks/mu-tools/recipes/google_api.rb +4 -0
  18. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  19. data/cookbooks/mu-tools/resources/disk.rb +33 -12
  20. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  21. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  22. data/extras/clean-stock-amis +10 -2
  23. data/extras/generate-stock-images +7 -3
  24. data/extras/image-generators/AWS/centos7.yaml +19 -16
  25. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  26. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  27. data/modules/mommacat.ru +2 -2
  28. data/modules/mu.rb +84 -97
  29. data/modules/mu/adoption.rb +359 -59
  30. data/modules/mu/cleanup.rb +67 -44
  31. data/modules/mu/cloud.rb +108 -1754
  32. data/modules/mu/cloud/database.rb +49 -0
  33. data/modules/mu/cloud/dnszone.rb +44 -0
  34. data/modules/mu/cloud/machine_images.rb +212 -0
  35. data/modules/mu/cloud/providers.rb +81 -0
  36. data/modules/mu/cloud/resource_base.rb +929 -0
  37. data/modules/mu/cloud/server.rb +40 -0
  38. data/modules/mu/cloud/server_pool.rb +1 -0
  39. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  40. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  41. data/modules/mu/cloud/wrappers.rb +178 -0
  42. data/modules/mu/config.rb +122 -80
  43. data/modules/mu/config/alarm.rb +2 -6
  44. data/modules/mu/config/bucket.rb +32 -3
  45. data/modules/mu/config/cache_cluster.rb +2 -2
  46. data/modules/mu/config/cdn.rb +100 -0
  47. data/modules/mu/config/collection.rb +1 -1
  48. data/modules/mu/config/container_cluster.rb +2 -2
  49. data/modules/mu/config/database.rb +84 -105
  50. data/modules/mu/config/database.yml +1 -2
  51. data/modules/mu/config/dnszone.rb +5 -4
  52. data/modules/mu/config/doc_helpers.rb +4 -5
  53. data/modules/mu/config/endpoint.rb +2 -1
  54. data/modules/mu/config/firewall_rule.rb +3 -19
  55. data/modules/mu/config/folder.rb +1 -1
  56. data/modules/mu/config/function.rb +17 -8
  57. data/modules/mu/config/group.rb +1 -1
  58. data/modules/mu/config/habitat.rb +1 -1
  59. data/modules/mu/config/job.rb +89 -0
  60. data/modules/mu/config/loadbalancer.rb +57 -11
  61. data/modules/mu/config/log.rb +1 -1
  62. data/modules/mu/config/msg_queue.rb +1 -1
  63. data/modules/mu/config/nosqldb.rb +1 -1
  64. data/modules/mu/config/notifier.rb +8 -19
  65. data/modules/mu/config/ref.rb +81 -9
  66. data/modules/mu/config/role.rb +1 -1
  67. data/modules/mu/config/schema_helpers.rb +30 -34
  68. data/modules/mu/config/search_domain.rb +1 -1
  69. data/modules/mu/config/server.rb +5 -13
  70. data/modules/mu/config/server_pool.rb +3 -7
  71. data/modules/mu/config/storage_pool.rb +1 -1
  72. data/modules/mu/config/tail.rb +10 -0
  73. data/modules/mu/config/user.rb +1 -1
  74. data/modules/mu/config/vpc.rb +13 -17
  75. data/modules/mu/defaults/AWS.yaml +106 -106
  76. data/modules/mu/defaults/Azure.yaml +1 -0
  77. data/modules/mu/defaults/Google.yaml +1 -0
  78. data/modules/mu/deploy.rb +33 -19
  79. data/modules/mu/groomer.rb +15 -0
  80. data/modules/mu/groomers/chef.rb +3 -0
  81. data/modules/mu/logger.rb +120 -144
  82. data/modules/mu/master.rb +22 -1
  83. data/modules/mu/mommacat.rb +71 -26
  84. data/modules/mu/mommacat/daemon.rb +23 -14
  85. data/modules/mu/mommacat/naming.rb +82 -3
  86. data/modules/mu/mommacat/search.rb +59 -16
  87. data/modules/mu/mommacat/storage.rb +119 -48
  88. data/modules/mu/{clouds → providers}/README.md +1 -1
  89. data/modules/mu/{clouds → providers}/aws.rb +248 -62
  90. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  91. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  92. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  93. data/modules/mu/providers/aws/cdn.rb +782 -0
  94. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  95. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +65 -63
  96. data/modules/mu/providers/aws/database.rb +1747 -0
  97. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  98. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  99. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  100. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  101. data/modules/mu/{clouds → providers}/aws/function.rb +291 -133
  102. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  103. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  104. data/modules/mu/providers/aws/job.rb +469 -0
  105. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  106. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  107. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  108. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  109. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  110. data/modules/mu/{clouds → providers}/aws/role.rb +112 -78
  111. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  112. data/modules/mu/{clouds → providers}/aws/server.rb +120 -145
  113. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  114. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  115. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  116. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  117. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  118. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  119. data/modules/mu/{clouds → providers}/aws/vpc.rb +141 -73
  120. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  121. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  122. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  123. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  124. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  126. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  127. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  128. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  129. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  130. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  132. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  133. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  134. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  135. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  136. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  137. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  138. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  142. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  143. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  144. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  145. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  146. data/modules/mu/{clouds → providers}/google.rb +15 -6
  147. data/modules/mu/{clouds → providers}/google/bucket.rb +2 -2
  148. data/modules/mu/{clouds → providers}/google/container_cluster.rb +29 -14
  149. data/modules/mu/{clouds → providers}/google/database.rb +2 -9
  150. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +3 -3
  151. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  152. data/modules/mu/{clouds → providers}/google/function.rb +4 -4
  153. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  154. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  155. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +2 -2
  156. data/modules/mu/{clouds → providers}/google/role.rb +46 -35
  157. data/modules/mu/{clouds → providers}/google/server.rb +26 -11
  158. data/modules/mu/{clouds → providers}/google/server_pool.rb +11 -11
  159. data/modules/mu/{clouds → providers}/google/user.rb +32 -22
  160. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  161. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  162. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  163. data/modules/mu/{clouds → providers}/google/vpc.rb +38 -3
  164. data/modules/tests/aws-jobs-functions.yaml +46 -0
  165. data/modules/tests/centos6.yaml +15 -0
  166. data/modules/tests/centos7.yaml +15 -0
  167. data/modules/tests/centos8.yaml +12 -0
  168. data/modules/tests/ecs.yaml +2 -2
  169. data/modules/tests/eks.yaml +1 -1
  170. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  171. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  172. data/modules/tests/microservice_app.yaml +288 -0
  173. data/modules/tests/rds.yaml +108 -0
  174. data/modules/tests/regrooms/rds.yaml +123 -0
  175. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  176. data/modules/tests/super_complex_bok.yml +2 -2
  177. data/modules/tests/super_simple_bok.yml +2 -2
  178. data/spec/mu/clouds/azure_spec.rb +2 -2
  179. metadata +126 -98
  180. data/modules/mu/clouds/aws/database.rb +0 -1974
  181. data/modules/mu/clouds/aws/endpoint.rb +0 -596
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 447346eba4a1cd7ee0df18c2c85aea32d1a45af0f4d22474fc902007d1f30a2c
4
- data.tar.gz: 8f44c2cf180c0748c712b9c244d0c21335147c6a3c9f6a1472772546e13a9b85
3
+ metadata.gz: c0a85c9f70be756955896aaeb1ea32d462178402d4eec97279454337f839fc96
4
+ data.tar.gz: 3bee42f370ebb5ac6caa2fb52a36ec61d4aae204410a4aed13472cad130e222a
5
5
  SHA512:
6
- metadata.gz: 8008e86471d5596337e3b642f5740a2bfe3b178646dd36a37f23dcfb8e0eacfcfbab4bac148ec5855f74f803543fd333480636249ec938387523e6f0c1fddde8
7
- data.tar.gz: '08466a848ca7b54fc6460e240f472cc812e93730650545f240a92ea67bde875b96a5d8398b770182355721c6628887665f938dcb7a3103209f7e170cffa246b9'
6
+ metadata.gz: 2910888a4c3061b4536bd84d60ec2c6b2b4170043983c603d5ff5b0af22ed43adee32e283e13da844fd949a9761a88c171a1eeb6b2b99bd7a08a93ed1efae772
7
+ data.tar.gz: 5b6e371475a5768895d5618865d42d5005265fa0810d5bf71e2af93032bb2767e843612ef7ec9e401b7be48dc10827cb3445bd005cdc9e4a8dd088488f30fed7
data/Dockerfile CHANGED
@@ -8,7 +8,7 @@ RUN df -h
8
8
 
9
9
  RUN apt-get update
10
10
 
11
- RUN apt-get install -y ruby2.5-dev dnsutils ansible build-essential python-pip curl
11
+ RUN apt-get install -y ruby2.5-dev dnsutils ansible build-essential python-pip curl openssh-client
12
12
 
13
13
  RUN apt-get upgrade -y
14
14
 
@@ -21,12 +21,6 @@ require 'bundler/setup'
21
21
  require 'optimist'
22
22
  require 'mu'
23
23
 
24
- available_clouds = MU::Cloud.supportedClouds
25
- available_clouds.reject! { |cloud|
26
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
27
- cloudclass.listCredentials.nil? or cloudclass.listCredentials.size == 0
28
- }
29
-
30
24
  available_types = MU::Cloud.resource_types.keys.map { |t| t.to_s }
31
25
  grouping_options = {
32
26
  "logical" => "Group resources in logical layers (folders and habitats together, users/roles/groups together, network resources together, etc)",
@@ -39,16 +33,19 @@ $opt = Optimist::options do
39
33
  EOS
40
34
  opt :appname, "The overarching name of the application stack we will generate", :required => false, :default => "mu", :type => :string
41
35
  opt :types, "The resource types to scan and import. Valid types: #{available_types.join(", ")}", :required => false, :type => :strings, :default => available_types
42
- opt :clouds, "The cloud providers to scan and import.", :required => false, :type => :strings, :default => available_clouds
36
+ opt :clouds, "The cloud providers to scan and import.", :required => false, :type => :strings, :default => MU::Cloud.availableClouds
43
37
  opt :parent, "Where applicable, resources which reside in the root folder or organization are configured with the specified parent in our target BoK", :required => false, :type => :string
44
38
  opt :billing, "Force-set this billing entity on created resources, instead of copying from the live resources", :required => false, :type => :string
45
39
  opt :sources, "One or more sets of credentials to use when importing resources. By default we will search and import from all sets of available credentials for each cloud provider specified with --clouds", :required => false, :type => :strings
46
40
  opt :credentials, "Override the 'credentials' value in our generated Baskets of Kittens to target a single, specific account. Our default behavior is to set each resource to deploy into the account from which it was sourced.", :required => false, :type => :string
47
41
  opt :savedeploys, "Generate actual deployment metadata in #{MU.dataDir}/deployments, as though the resources we found were created with mu-deploy. If we are generating more than one configuration, and a resource needs to reference another resource (e.g. to declare a VPC in which to reside), this will allow us to reference them as virtual resource, rather than by raw cloud identifier.", :required => false, :type => :boolean, :default => false
48
42
  opt :diff, "List the differences between what we find and an existing, saved deploy from a previous run, if one exists.", :required => false, :type => :boolean
43
+ opt :merge_changes, "When using --diff, merge detected changes into the baseline deploy after reporting on them.", :required => false, :type => :boolean, :default => false
49
44
  opt :grouping, "Methods for grouping found resources into separate Baskets.\n\n"+MU::Adoption::GROUPMODES.keys.map { |g| "* "+g.to_s+": "+MU::Adoption::GROUPMODES[g] }.join("\n")+"\n\n", :required => false, :type => :string, :default => "logical"
50
45
  opt :habitats, "Limit scope of searches to the named accounts/projects/subscriptions, instead of search all habitats visible to our credentials.", :required => false, :type => :strings
46
+ opt :regions, "Restrict to operating on a subset of available regions, instead of all that we know about.", :require => false, :type => :strings
51
47
  opt :scrub, "Whether to set scrub_mu_isms in the BoKs we generate", :default => $MU_CFG.has_key?('adopt_scrub_mu_isms') ? $MU_CFG['adopt_scrub_mu_isms'] : false
48
+ opt :pattern, "Only adopt resources whose resource name would match this pattern. Must be a valid regular expression. Alphabetical characters will be treated case-insensitively.", :required => false, :type => :string
52
49
  end
53
50
 
54
51
  ok = true
@@ -64,6 +61,16 @@ if $opt[:diff]
64
61
  $opt[:savedeploys] = false
65
62
  end
66
63
 
64
+ pattern = nil
65
+ if $opt[:pattern]
66
+ begin
67
+ pattern = Regexp.new($opt[:pattern], true)
68
+ rescue RegexpError => e
69
+ MU.log "Invalid --pattern option: #{e.message}", MU::ERR
70
+ exit 1
71
+ end
72
+ end
73
+
67
74
  types = []
68
75
  $opt[:types].each { |t|
69
76
  t_name = t.gsub(/-/, "_")
@@ -102,8 +109,7 @@ if !ok
102
109
  exit 1
103
110
  end
104
111
 
105
-
106
- adoption = MU::Adoption.new(clouds: clouds, types: types, parent: $opt[:parent], billing: $opt[:billing], sources: $opt[:sources], credentials: $opt[:credentials], group_by: $opt[:grouping].to_sym, savedeploys: $opt[:savedeploys], diff: $opt[:diff], habitats: $opt[:habitats], scrub_mu_isms: $opt[:scrub])
112
+ adoption = MU::Adoption.new(clouds: clouds, types: types, parent: $opt[:parent], billing: $opt[:billing], sources: $opt[:sources], credentials: $opt[:credentials], group_by: $opt[:grouping].to_sym, savedeploys: $opt[:savedeploys], diff: $opt[:diff], habitats: $opt[:habitats], scrub_mu_isms: $opt[:scrub], regions: $opt[:regions], merge: $opt[:merge_changes], pattern: pattern)
107
113
  found = adoption.scrapeClouds
108
114
  if found.nil? or found.empty?
109
115
  MU.log "No resources found to adopt", MU::WARN, details: {"clouds" => clouds, "types" => types }
@@ -117,10 +123,7 @@ boks.each_pair { |appname, bok|
117
123
  File.open("#{appname}.yaml", "w") { |f|
118
124
  f.write JSON.parse(JSON.generate(bok)).to_yaml
119
125
  }
120
- conf_engine = MU::Config.new("#{appname}.yaml")
121
- stack_conf = conf_engine.config
122
126
  # puts stack_conf.to_yaml
123
- MU.log "#{appname}.yaml validated successfully", MU::NOTICE
124
127
  MU::Cloud.resource_types.each_pair { |type, cfg|
125
128
  if bok[cfg[:cfg_plural]]
126
129
  MU.log "#{bok[cfg[:cfg_plural]].size.to_s} #{cfg[:cfg_plural]}", MU::NOTICE
@@ -0,0 +1,57 @@
1
+ #!/usr/local/ruby-current/bin/ruby
2
+ # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
3
+ #
4
+ # Licensed under the BSD-3 license (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License in the root of the project or at
7
+ #
8
+ # http://egt-labs.com/mu/LICENSE.html
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'rubygems'
17
+ require 'bundler/setup'
18
+ require 'json'
19
+ require 'erb'
20
+ require 'optimist'
21
+ require 'json-schema'
22
+ require File.realpath(File.expand_path(File.dirname(__FILE__)+"/mu-load-config.rb"))
23
+ require 'mu'
24
+
25
+ (0..100000).to_a.each { |n|
26
+ retries = 0
27
+ seed = nil
28
+ # begin
29
+ # raise MuError, "Failed to allocate an unused MU-ID after #{retries} tries!" if retries > 70
30
+ # seedsize = 1 + (retries/10).abs
31
+ # seed = (0...seedsize+1).map { ('a'..'z').to_a[rand(26)] }.join
32
+ # end while seed == "mu" or seed[0] == seed[1]
33
+ seed = "nn"
34
+ handle = MU::MommaCat.generateHandle(seed)
35
+ puts handle
36
+ }
37
+ exit
38
+
39
+ #pp MU::Cloud::Azure.listRegions
40
+ #pp MU::Cloud::Azure::Habitat.testcalls
41
+ #pp MU::Cloud::Azure::VPC.find(cloud_id: MU::Cloud::Azure::Id.new(resource_group: "mu", name: "mu-vnet"))
42
+ #pp MU::Cloud::Azure.authorization.role_assignments.list_for_resource_group("AKS-DEV-2019062015-KA-EASTUS")
43
+ #pp MU::Cloud::Azure::Role.find(role_name: "Azure Kubernetes Service Cluster Admin Role")
44
+ #puts MU::Cloud::Azure.default_subscription
45
+ #pp MU::Cloud::Azure.fetchPublicIP("MYVPC-DEV-2019061911-XI-EASTUS", "ip-addr-thingy")
46
+ #pp MU::Cloud::Azure.ensureProvider("egtazure", "Microsoft.ContainerService", force: true)
47
+ pp MU::Cloud::Azure::Server.find(cloud_id: "mu")
48
+ exit
49
+ pp MU::Cloud::Azure::Server.fetchImage("OpenLogic/CentOS/6")
50
+ pp MU::Cloud::Azure::Server.fetchImage("OpenLogic/CentOS/7")
51
+ pp MU::Cloud::Azure::Server.fetchImage("RedHat/RHEL/8")
52
+ pp MU::Cloud::Azure::Server.fetchImage("RedHat/RHEL/7")
53
+ pp MU::Cloud::Azure::Server.fetchImage("RedHat/RHEL/6")
54
+ pp MU::Cloud::Azure::Server.fetchImage("Debian/debian-10/10")
55
+ pp MU::Cloud::Azure::Server.fetchImage("MicrosoftWindowsServer/WindowsServer/2012-R2-Datacenter")
56
+ pp MU::Cloud::Azure::Server.fetchImage("MicrosoftWindowsServer/WindowsServer/2016-Datacenter")
57
+ pp MU::Cloud::Azure::Server.fetchImage("MicrosoftWindowsServer/WindowsServer/2019-Datacenter")
@@ -24,10 +24,8 @@ require 'mu'
24
24
  Dir.chdir(MU.installDir)
25
25
 
26
26
  credentials = []
27
- MU::Cloud.supportedClouds.each { |cloud|
28
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
29
- next if cloudclass.listCredentials.nil? or cloudclass.listCredentials.size == 0
30
- credentials.concat(cloudclass.listCredentials)
27
+ MU::Cloud.availableClouds.each { |cloud|
28
+ credentials.concat(MU::Cloud.cloudClass(cloud).listCredentials)
31
29
  }
32
30
  credentials.uniq!
33
31
 
@@ -113,12 +113,44 @@ $CONFIGURABLES = {
113
113
  "desc" => "Disable the Momma Cat grooming daemon. Nodes which require asynchronous Ansible/Chef bootstraps will not function. This option is only honored in gem-based installations.",
114
114
  "boolean" => true
115
115
  },
116
+ "adopt_change_notify" => {
117
+ "title" => "Adoption Change Notifications",
118
+ "subtree" => {
119
+ "slack" => {
120
+ "title" => "Send to Slack",
121
+ "desc" => "Report modifications to adopted resources, detected by mu-adopt --diff, to the Slack webhook and channel configured under Slack Configuration.",
122
+ "boolean" => true
123
+ },
124
+ "slack_snippet_threshold" => {
125
+ "title" => "Attachment Threshold",
126
+ "desc" => "If a list of details about a modified resources is longer than this number of lines (in JSON), it will be sent as an \"attachment,\" which in Slack means a blockquote that displays a few lines with a \"Show more\" button. The internal default is 5 lines."
127
+ },
128
+ # "email" => {
129
+ # "title" => "Send Email",
130
+ # "desc" => "",
131
+ # "boolean" => true
132
+ # }
133
+ }
134
+ },
116
135
  "adopt_scrub_mu_isms" => {
117
- "title" => "Disable Momma Cat",
136
+ "title" => "Scrub Mu-isms from Baskets of Kittens",
118
137
  "default" => false,
119
138
  "desc" => "Ordinarily, Mu will automatically name, tag and generate auxiliary resources in a standard Mu-ish fashion that allows for deployment of multiple clones of a given stack. Toggling this flag will change the default behavior of mu-adopt, when it creates stack descriptors from found resources, to enable or disable this behavior (see also mu-adopt's --scrub option).",
120
139
  "boolean" => true
121
140
  },
141
+ "slack" => {
142
+ "title" => "Slack Configuration",
143
+ "subtree" => {
144
+ "webhook" => {
145
+ "title" => "Webhook",
146
+ "desc" => "The hooks.slack.com URL for the webook to which we'll send deploy notifications"
147
+ },
148
+ "channel" => {
149
+ "title" => "Channel",
150
+ "desc" => "The channel name (without leading #) to which alerts should be sent."
151
+ }
152
+ }
153
+ },
122
154
  "mommacat_port" => {
123
155
  "title" => "Momma Cat Listen Port",
124
156
  "pattern" => /^[0-9]+$/i,
@@ -247,6 +279,10 @@ $CONFIGURABLES = {
247
279
  "required" => false,
248
280
  "desc" => "For Google Cloud projects which are attached to a GSuite domain. GCP service accounts cannot view or manage GSuite resources (groups, users, etc) directly, but must instead masquerade as a GSuite user which has delegated authority to the service account. See also: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority"
249
281
  },
282
+ "org" => {
283
+ "title" => "Default Org/Domain",
284
+ "desc" => "For credential sets which have access to multiple GSuite or Cloud Identity orgs, you must specify a default organization (e.g. my.domain.com)."
285
+ },
250
286
  "customer_id" => {
251
287
  "title" => "GSuite Customer ID",
252
288
  "required" => false,
@@ -105,7 +105,7 @@ if $opts[:dryrun]
105
105
  Thread.handle_interrupt(MU::Cloud::MuCloudResourceNotImplemented => :never) {
106
106
  begin
107
107
  Thread.handle_interrupt(MU::Cloud::MuCloudResourceNotImplemented => :immediate) {
108
- MU.log "Cost calculator not available for this stack, as it uses a resource not implemented in Mu's CloudFormation layer.", MU::WARN, verbosity: MU::Logger::NORMAL
108
+ MU.log "Cost calculator not available for this stack, as it uses a resource not implemented in Mu's CloudFormation layer.", MU::NOTICE, verbosity: MU::Logger::NORMAL
109
109
  Thread.current.exit
110
110
  }
111
111
  ensure
@@ -124,7 +124,7 @@ if $opts[:dryrun]
124
124
  )
125
125
  cost_dummy_deploy.run
126
126
  rescue MU::Cloud::MuCloudResourceNotImplemented, MU::Cloud::MuCloudFlagNotImplemented
127
- MU.log "Cost calculator not available for this stack, as it uses a resource not implemented in Mu's CloudFormation layer.", MU::WARN, verbosity: MU::Logger::NORMAL
127
+ MU.log "Cost calculator not available for this stack, as it uses a resource not implemented in Mu's CloudFormation layer.", MU::NOTICE, verbosity: MU::Logger::NORMAL
128
128
  end
129
129
  end
130
130
  exit
@@ -135,7 +135,7 @@ if $opts[:update]
135
135
  # TODO consider whether this is useful/valid
136
136
  # old_conf = JSON.parse(File.read(deploy.deploy_dir+"/basket_of_kittens.json"))
137
137
  # stack_conf = old_conf.merge(stack_conf)
138
- deploy.updateBasketofKittens(stack_conf)
138
+ deploy.updateBasketofKittens(stack_conf, skip_validation: true)
139
139
  deployer = MU::Deploy.new(
140
140
  deploy.environment,
141
141
  verbosity: verbosity,
@@ -0,0 +1,25 @@
1
+ #!/usr/local/ruby-current/bin/ruby
2
+ # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
3
+ #
4
+ # Licensed under the BSD-3 license (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License in the root of the project or at
7
+ #
8
+ # http://egt-labs.com/mu/LICENSE.html
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'rubygems'
17
+ require 'bundler/setup'
18
+ require 'json'
19
+ require 'erb'
20
+ require 'optimist'
21
+ require 'json-schema'
22
+ require File.realpath(File.expand_path(File.dirname(__FILE__)+"/mu-load-config.rb"))
23
+ require 'mu'
24
+
25
+ MU::MommaCat.findStray("AWS", "firewall_rule", region: MU.myRegion, dummy_ok: true, debug: true)
@@ -79,8 +79,7 @@ EOF
79
79
  impl_counts[type] ||= 0
80
80
  [a, b].each { |cloud|
81
81
  begin
82
- myclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(type)
83
- case myclass.quality
82
+ case MU::Cloud.resourceClass(cloud, type).quality
84
83
  when MU::Cloud::RELEASE
85
84
  cloud_is_useful[cloud] = true
86
85
  counts[cloud] += 4
@@ -114,8 +113,7 @@ EOF
114
113
  cloudlist.each { |cloud|
115
114
  readme += "<td><center>"
116
115
  begin
117
- myclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(type)
118
- case myclass.quality
116
+ case MU::Cloud.resourceClass(cloud, type).quality
119
117
  when MU::Cloud::RELEASE
120
118
  readme += "<img src='release.png' style='#{icon_style}' title='Release Quality' alt='[Release Quality]'>"
121
119
  when MU::Cloud::BETA
@@ -134,7 +134,7 @@ def loadMuConfig(default_cfg_overrides = nil)
134
134
  }
135
135
  end
136
136
 
137
- global_cfg = { "config_files" => [] }
137
+ global_cfg = { "config_files" => [], "overridden_keys" => [] }
138
138
  if File.exist?(cfgPath)
139
139
  global_cfg = YAML.load(File.read(cfgPath))
140
140
  global_cfg["config_files"] = [cfgPath]
@@ -147,6 +147,7 @@ def loadMuConfig(default_cfg_overrides = nil)
147
147
  if localfile
148
148
  global_cfg.merge!(localfile)
149
149
  global_cfg["config_files"] << "#{home}/.mu.yaml"
150
+ global_cfg["overridden_keys"] = localfile.keys
150
151
  end
151
152
  end
152
153
  if !global_cfg.has_key?("installdir")
@@ -34,6 +34,7 @@ Usage:
34
34
  #{$0} [-m <#>] [-f] [-v] [specific test BoK to run [...]]
35
35
  EOS
36
36
  opt :max_threads, "Environment to set on creation.", :require => false, :default => 3, :type => :integer
37
+ opt :max_retries, "Number of times to retry failed tests in --dryrun mode.", :require => false, :default => 2, :type => :integer
37
38
  opt :full, "Actually run deploys, instead of --dryrun", :require => false, :default => false
38
39
  opt :verbose, "Show more information while running", :require => false, :default => false
39
40
  end
@@ -42,7 +43,7 @@ only = ARGV
42
43
 
43
44
  files = Dir.glob("*.yaml", base: dir)
44
45
  files.concat(Dir.glob("*.yml", base: dir))
45
- baseclouds = MU::Cloud.supportedClouds.reject { |c| c == "CloudFormation" }
46
+ baseclouds = MU::Cloud.availableClouds.reject { |c| c == "CloudFormation" }
46
47
 
47
48
  commands = {}
48
49
  failures = []
@@ -56,20 +57,33 @@ end
56
57
 
57
58
  files.each { |f|
58
59
  clouds = baseclouds.dup
60
+ groomer_match = true
59
61
  File.open(dir+"/"+f).readlines.each { |l|
60
62
  l.chomp!
61
- next if !l.match(/^\s*#\s*clouds: (.*)/)
62
- clouds = []
63
- cloudstr = Regexp.last_match[1]
64
- cloudstr.split(/\s*,\s*/).each { |c|
65
- baseclouds.each { |cloud|
66
- if cloud.match(/^#{Regexp.quote(c)}$/i)
67
- clouds << cloud
63
+ if l.match(/^\s*#\s*clouds: (.*)/)
64
+ clouds = []
65
+ cloudstr = Regexp.last_match[1]
66
+ cloudstr.split(/\s*,\s*/).each { |c|
67
+ baseclouds.each { |cloud|
68
+ if cloud.match(/^#{Regexp.quote(c)}$/i)
69
+ clouds << cloud
70
+ end
71
+ }
72
+ }
73
+ elsif l.match(/^\s*#\s*groomers: (.*)/)
74
+ groomerstr = Regexp.last_match[1]
75
+ groomerstr.split(/\s*,\s*/).each { |g|
76
+ if !MU::Groomer.availableGroomers.include?(g)
77
+ MU.log "#{f} requires groomer #{g}, which is not available. This test will be skipped.", MU::NOTICE
78
+ groomer_match = false
68
79
  end
69
80
  }
70
- }
71
- break
81
+ end
72
82
  }
83
+ if !groomer_match
84
+ next
85
+ end
86
+
73
87
  clouds.each { |cloud|
74
88
  cmd = "mu-deploy #{f} --cloud #{cloud} #{$opts[:full] ? "" : "--dryrun"}"
75
89
  commands[cmd] = {
@@ -108,8 +122,19 @@ def execCommand(cmd, results_stash)
108
122
  }
109
123
 
110
124
  ok = true
111
- output = %x{#{cmd} 2>&1}
112
- ok = false if $?.exitstatus != 0
125
+ retries = 0
126
+ begin
127
+ output = %x{#{cmd} 2>&1}
128
+ if $?.exitstatus != 0
129
+ ok = false
130
+ retries += 1
131
+ if $opts[:verbose] and !$opts[:full] and retries <= $opts[:max_retries]
132
+ puts "#{cmd} RETRY #{retries.to_s}".light_red
133
+ end
134
+ else
135
+ ok = true
136
+ end
137
+ end while !ok and !$opts[:full] and retries <= $opts[:max_retries]
113
138
 
114
139
  results_stash["output"] += output
115
140
 
@@ -17,8 +17,8 @@ end
17
17
 
18
18
  Gem::Specification.new do |s|
19
19
  s.name = 'cloud-mu'
20
- s.version = '3.1.6'
21
- s.date = '2020-03-20'
20
+ s.version = '3.4.0'
21
+ s.date = '2020-10-22'
22
22
  s.require_paths = ['modules']
23
23
  s.required_ruby_version = '>= 2.4'
24
24
  s.summary = "The eGTLabs Mu toolkit for unified cloud deployments"
@@ -36,7 +36,7 @@ EOF
36
36
  'https://github.com/cloudamatic/mu'
37
37
  s.license = 'BSD-3-Clause-Attribution'
38
38
  s.add_runtime_dependency 'addressable', '~> 2.5'
39
- s.add_runtime_dependency "aws-sdk-core", "< 3"
39
+ s.add_runtime_dependency "aws-sdk", "~> 3.0"
40
40
  s.add_runtime_dependency 'azure_sdk', "~> 0.52"
41
41
  s.add_runtime_dependency 'bundler', "~> 1.17"
42
42
  s.add_runtime_dependency 'chronic_duration', "~> 0.10"
@@ -57,7 +57,7 @@ EOF
57
57
  s.add_runtime_dependency 'rack', "~> 2.0"
58
58
  s.add_runtime_dependency 'ruby-graphviz', "~> 1.2"
59
59
  s.add_runtime_dependency 'rubocop', '~> 0.58'
60
- s.add_runtime_dependency 'rubyzip', "~> 2.0"
60
+ s.add_runtime_dependency 'rubyzip', "~> 2.3"
61
61
  s.add_runtime_dependency 'simple-password-gen', "~> 0.1"
62
62
  s.add_runtime_dependency 'slack-notifier', "~> 2.3"
63
63
  s.add_runtime_dependency 'solve', '~> 4.0'
@@ -21,6 +21,13 @@ if disk_name_str == "CAP-MASTER" or disk_name_str == "MU-MASTER" and !node['host
21
21
  disk_name_str = node['hostname']
22
22
  end rescue NoMethodError
23
23
 
24
+ diskdevs = :xvd
25
+ if !platform_family?("windows")
26
+ if default['kernel']['modules'].keys.include?("nvme")
27
+ diskdevs = :nvme
28
+ end
29
+ end
30
+
24
31
  default['os_updates_using_chef'] = false
25
32
 
26
33
  default['application_attributes']['application_volume']['mount_directory'] = '/apps'
@@ -45,6 +45,70 @@ module Mutools
45
45
  nil
46
46
  end
47
47
 
48
+ # Just list our block devices
49
+ # @return [Array<String>]
50
+ def list_disk_devices
51
+ if File.executable?("/bin/lsblk")
52
+ shell_out(%Q{/bin/lsblk -i -p -r -n | egrep ' disk( |$)'}).stdout.each_line.map { |l|
53
+ l.chomp.sub(/ .*/, '')
54
+ }
55
+ else
56
+ # XXX something dumber
57
+ nil
58
+ end
59
+ end
60
+
61
+ # If we're in AWS and NVME-aware, return a mapping of AWS-side device names
62
+ # to actual NVME devices.
63
+ # @return [Hash]
64
+ def attached_nvme_disks
65
+ if get_aws_metadata("meta-data/instance-id").nil? or
66
+ !File.executable?("/bin/lsblk") or !File.executable?("/sbin/nvme")
67
+ return {}
68
+ end
69
+ map = {}
70
+ devices = list_disk_devices
71
+ return {} if !devices
72
+ devices.each { |d|
73
+ if d =~ /^\/dev\/nvme/
74
+ shell_out(%Q{/sbin/nvme id-ctrl -v #{d}}).stdout.each_line { |desc|
75
+ if desc.match(/^0000: (?:[0-9a-f]{2} ){16}"(.+?)\./)
76
+ virt_dev = Regexp.last_match[1]
77
+ map[virt_dev] = d
78
+ if !File.exists?(virt_dev)
79
+ begin
80
+ File.symlink(d, virt_dev)
81
+ rescue Errno::EEXIST # XXX whyyyyy is this needed
82
+ end
83
+ end
84
+ break
85
+ end
86
+ }
87
+ end
88
+ }
89
+ map
90
+ end
91
+
92
+ def real_devicepath(dev)
93
+ map = attached_nvme_disks
94
+ if map[dev]
95
+ map[dev]
96
+ else
97
+ dev # be nice to actually handle this too
98
+ end
99
+ end
100
+
101
+ def nvme?
102
+ if File.executable?("/bin/lsblk")
103
+ shell_out(%Q{/bin/lsblk -i -p -r -n}).stdout.each_line { |l|
104
+ return true if l =~ /^\/dev\/nvme\d/
105
+ }
106
+ else
107
+ return true if File.exists?("/dev/nvme0n1")
108
+ end
109
+ false
110
+ end
111
+
48
112
  @project = nil
49
113
  @authorizer = nil
50
114
  def set_gcp_cfg_params
@@ -186,12 +250,12 @@ module Mutools
186
250
  if cloud == "AWS"
187
251
  resp = nil
188
252
  begin
253
+ Chef::Log.info("Fetch deploy secret from s3://#{bucket}/#{filename}")
189
254
  resp = s3.get_object(bucket: bucket, key: filename)
190
255
  rescue ::Aws::S3::Errors::PermanentRedirect => e
191
256
  tmps3 = Aws::S3::Client.new(region: "us-east-1")
192
257
  resp = tmps3.get_object(bucket: bucket, key: filename)
193
258
  end
194
- Chef::Log.info("Fetch deploy secret from s3://#{bucket}/#{filename}")
195
259
  secret = resp.body.read
196
260
  elsif cloud == "Google"
197
261
  include_recipe "mu-tools::gcloud"
@@ -230,17 +294,20 @@ module Mutools
230
294
  end
231
295
 
232
296
  def mommacat_request(action, arg)
297
+ params = Base64.urlsafe_encode64(JSON.generate(arg)) if arg
233
298
  uri = URI("https://#{get_mu_master_ips.first}:2260/")
234
299
  req = Net::HTTP::Post.new(uri)
235
300
  res_type = (node['deployment'].has_key?(:server_pools) and node['deployment']['server_pools'].has_key?(node['service_name'])) ? "server_pool" : "server"
236
301
  response = nil
237
302
  begin
238
303
  secret = get_deploy_secret
239
- if secret.nil?
304
+ if secret.nil? or secret.empty?
240
305
  raise "Failed to fetch deploy secret, and I can't communicate with Momma Cat without it"
241
306
  end
242
307
 
243
308
  Chef::Log.info("Sending Momma Cat #{action} request to #{uri} from #{get_aws_metadata("meta-data/instance-id")}")
309
+ disks_before = list_disk_devices if action == "add_volume"
310
+
244
311
  req.set_form_data(
245
312
  "mu_id" => mu_get_tag_value("MU-ID"),
246
313
  "mu_resource_name" => node['service_name'],
@@ -248,7 +315,7 @@ module Mutools
248
315
  "mu_resource_type" => res_type,
249
316
  "mu_user" => node['deployment']['mu_user'] || node['deployment']['chef_user'],
250
317
  "mu_deploy_secret" => secret,
251
- action => arg
318
+ action => params
252
319
  )
253
320
  http = Net::HTTP.new(uri.hostname, uri.port)
254
321
  http.use_ssl = true
@@ -256,6 +323,23 @@ module Mutools
256
323
  response = http.request(req)
257
324
  if response.code != "200"
258
325
  Chef::Log.error("Got #{response.code} back from #{uri} on #{action} => #{arg}")
326
+ else
327
+ if action == "add_volume" and arg and arg.is_a?(Hash) and arg[:dev]
328
+ seen_requested = false
329
+ retries = 0
330
+ begin
331
+ list_disk_devices.each { |d|
332
+ if d == arg[:dev] or
333
+ (nvme? and d == attached_nvme_disks[arg[:dev]])
334
+ seen_requested = true
335
+ end
336
+ }
337
+ if !seen_requested
338
+ sleep 6
339
+ retries += 1
340
+ end
341
+ end while retries < 5 and !seen_requested
342
+ end
259
343
  end
260
344
  rescue EOFError => e
261
345
  # Sometimes deployment metadata is incomplete and missing a