cloud-mu 2.1.0beta → 3.0.0beta

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 (291) hide show
  1. checksums.yaml +5 -5
  2. data/Berksfile +4 -5
  3. data/Berksfile.lock +179 -0
  4. data/README.md +1 -6
  5. data/ansible/roles/geerlingguy.firewall/templates/firewall.bash.j2 +0 -0
  6. data/ansible/roles/mu-installer/README.md +33 -0
  7. data/ansible/roles/mu-installer/defaults/main.yml +2 -0
  8. data/ansible/roles/mu-installer/handlers/main.yml +2 -0
  9. data/ansible/roles/mu-installer/meta/main.yml +60 -0
  10. data/ansible/roles/mu-installer/tasks/main.yml +13 -0
  11. data/ansible/roles/mu-installer/tests/inventory +2 -0
  12. data/ansible/roles/mu-installer/tests/test.yml +5 -0
  13. data/ansible/roles/mu-installer/vars/main.yml +2 -0
  14. data/bin/mu-adopt +125 -0
  15. data/bin/mu-aws-setup +4 -4
  16. data/bin/mu-azure-setup +265 -0
  17. data/bin/mu-azure-tests +43 -0
  18. data/bin/mu-cleanup +20 -8
  19. data/bin/mu-configure +224 -98
  20. data/bin/mu-deploy +8 -3
  21. data/bin/mu-gcp-setup +16 -8
  22. data/bin/mu-gen-docs +92 -8
  23. data/bin/mu-load-config.rb +52 -12
  24. data/bin/mu-momma-cat +36 -0
  25. data/bin/mu-node-manage +34 -27
  26. data/bin/mu-self-update +2 -2
  27. data/bin/mu-ssh +12 -8
  28. data/bin/mu-upload-chef-artifacts +11 -4
  29. data/bin/mu-user-manage +3 -0
  30. data/cloud-mu.gemspec +8 -11
  31. data/cookbooks/firewall/libraries/helpers_iptables.rb +2 -2
  32. data/cookbooks/firewall/metadata.json +1 -1
  33. data/cookbooks/firewall/recipes/default.rb +5 -9
  34. data/cookbooks/mu-firewall/attributes/default.rb +2 -0
  35. data/cookbooks/mu-firewall/metadata.rb +1 -1
  36. data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +0 -0
  37. data/cookbooks/mu-master/Berksfile +2 -2
  38. data/cookbooks/mu-master/files/default/check_mem.pl +0 -0
  39. data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
  40. data/cookbooks/mu-master/metadata.rb +5 -4
  41. data/cookbooks/mu-master/recipes/389ds.rb +1 -1
  42. data/cookbooks/mu-master/recipes/basepackages.rb +30 -10
  43. data/cookbooks/mu-master/recipes/default.rb +59 -7
  44. data/cookbooks/mu-master/recipes/firewall-holes.rb +1 -1
  45. data/cookbooks/mu-master/recipes/init.rb +65 -47
  46. data/cookbooks/mu-master/recipes/{eks-kubectl.rb → kubectl.rb} +4 -10
  47. data/cookbooks/mu-master/recipes/sssd.rb +2 -1
  48. data/cookbooks/mu-master/recipes/update_nagios_only.rb +6 -6
  49. data/cookbooks/mu-master/templates/default/web_app.conf.erb +2 -2
  50. data/cookbooks/mu-master/templates/mods/ldap.conf.erb +4 -0
  51. data/cookbooks/mu-php54/Berksfile +1 -2
  52. data/cookbooks/mu-php54/metadata.rb +4 -5
  53. data/cookbooks/mu-php54/recipes/default.rb +1 -1
  54. data/cookbooks/mu-splunk/templates/default/splunk-init.erb +0 -0
  55. data/cookbooks/mu-tools/Berksfile +3 -2
  56. data/cookbooks/mu-tools/files/default/Mu_CA.pem +33 -0
  57. data/cookbooks/mu-tools/libraries/helper.rb +20 -8
  58. data/cookbooks/mu-tools/metadata.rb +5 -2
  59. data/cookbooks/mu-tools/recipes/apply_security.rb +2 -3
  60. data/cookbooks/mu-tools/recipes/eks.rb +1 -1
  61. data/cookbooks/mu-tools/recipes/gcloud.rb +5 -30
  62. data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
  63. data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -0
  64. data/cookbooks/mu-tools/recipes/selinux.rb +19 -0
  65. data/cookbooks/mu-tools/recipes/split_var_partitions.rb +0 -1
  66. data/cookbooks/mu-tools/recipes/windows-client.rb +256 -122
  67. data/cookbooks/mu-tools/resources/disk.rb +3 -1
  68. data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +1 -1
  69. data/cookbooks/mu-tools/templates/default/etc_hosts.erb +1 -1
  70. data/cookbooks/mu-tools/templates/default/{kubeconfig.erb → kubeconfig-eks.erb} +0 -0
  71. data/cookbooks/mu-tools/templates/default/kubeconfig-gke.erb +27 -0
  72. data/cookbooks/mu-tools/templates/windows-10/sshd_config.erb +137 -0
  73. data/cookbooks/mu-utility/recipes/nat.rb +4 -0
  74. data/extras/alpha.png +0 -0
  75. data/extras/beta.png +0 -0
  76. data/extras/clean-stock-amis +2 -2
  77. data/extras/generate-stock-images +131 -0
  78. data/extras/git-fix-permissions-hook +0 -0
  79. data/extras/image-generators/AWS/centos6.yaml +17 -0
  80. data/extras/image-generators/{aws → AWS}/centos7-govcloud.yaml +0 -0
  81. data/extras/image-generators/{aws → AWS}/centos7.yaml +0 -0
  82. data/extras/image-generators/{aws → AWS}/rhel7.yaml +0 -0
  83. data/extras/image-generators/{aws → AWS}/win2k12.yaml +0 -0
  84. data/extras/image-generators/{aws → AWS}/win2k16.yaml +0 -0
  85. data/extras/image-generators/{aws → AWS}/windows.yaml +0 -0
  86. data/extras/image-generators/{gcp → Google}/centos6.yaml +1 -0
  87. data/extras/image-generators/Google/centos7.yaml +18 -0
  88. data/extras/python_rpm/build.sh +0 -0
  89. data/extras/release.png +0 -0
  90. data/extras/ruby_rpm/build.sh +0 -0
  91. data/extras/ruby_rpm/muby.spec +1 -1
  92. data/install/README.md +43 -5
  93. data/install/deprecated-bash-library.sh +0 -0
  94. data/install/installer +1 -1
  95. data/install/jenkinskeys.rb +0 -0
  96. data/install/mu-master.yaml +55 -0
  97. data/modules/mommacat.ru +41 -7
  98. data/modules/mu.rb +444 -149
  99. data/modules/mu/adoption.rb +500 -0
  100. data/modules/mu/cleanup.rb +235 -158
  101. data/modules/mu/cloud.rb +675 -138
  102. data/modules/mu/clouds/aws.rb +156 -24
  103. data/modules/mu/clouds/aws/alarm.rb +4 -14
  104. data/modules/mu/clouds/aws/bucket.rb +60 -18
  105. data/modules/mu/clouds/aws/cache_cluster.rb +8 -20
  106. data/modules/mu/clouds/aws/collection.rb +12 -22
  107. data/modules/mu/clouds/aws/container_cluster.rb +209 -118
  108. data/modules/mu/clouds/aws/database.rb +120 -45
  109. data/modules/mu/clouds/aws/dnszone.rb +7 -18
  110. data/modules/mu/clouds/aws/endpoint.rb +5 -15
  111. data/modules/mu/clouds/aws/firewall_rule.rb +144 -72
  112. data/modules/mu/clouds/aws/folder.rb +4 -11
  113. data/modules/mu/clouds/aws/function.rb +6 -16
  114. data/modules/mu/clouds/aws/group.rb +4 -12
  115. data/modules/mu/clouds/aws/habitat.rb +11 -13
  116. data/modules/mu/clouds/aws/loadbalancer.rb +40 -28
  117. data/modules/mu/clouds/aws/log.rb +5 -13
  118. data/modules/mu/clouds/aws/msg_queue.rb +9 -24
  119. data/modules/mu/clouds/aws/nosqldb.rb +4 -12
  120. data/modules/mu/clouds/aws/notifier.rb +6 -13
  121. data/modules/mu/clouds/aws/role.rb +69 -40
  122. data/modules/mu/clouds/aws/search_domain.rb +17 -20
  123. data/modules/mu/clouds/aws/server.rb +184 -94
  124. data/modules/mu/clouds/aws/server_pool.rb +33 -38
  125. data/modules/mu/clouds/aws/storage_pool.rb +5 -12
  126. data/modules/mu/clouds/aws/user.rb +59 -33
  127. data/modules/mu/clouds/aws/userdata/linux.erb +18 -30
  128. data/modules/mu/clouds/aws/userdata/windows.erb +9 -9
  129. data/modules/mu/clouds/aws/vpc.rb +214 -145
  130. data/modules/mu/clouds/azure.rb +978 -44
  131. data/modules/mu/clouds/azure/container_cluster.rb +413 -0
  132. data/modules/mu/clouds/azure/firewall_rule.rb +500 -0
  133. data/modules/mu/clouds/azure/habitat.rb +167 -0
  134. data/modules/mu/clouds/azure/loadbalancer.rb +205 -0
  135. data/modules/mu/clouds/azure/role.rb +211 -0
  136. data/modules/mu/clouds/azure/server.rb +810 -0
  137. data/modules/mu/clouds/azure/user.rb +257 -0
  138. data/modules/mu/clouds/azure/userdata/README.md +4 -0
  139. data/modules/mu/clouds/azure/userdata/linux.erb +137 -0
  140. data/modules/mu/clouds/azure/userdata/windows.erb +275 -0
  141. data/modules/mu/clouds/azure/vpc.rb +782 -0
  142. data/modules/mu/clouds/cloudformation.rb +12 -9
  143. data/modules/mu/clouds/cloudformation/firewall_rule.rb +5 -13
  144. data/modules/mu/clouds/cloudformation/server.rb +10 -1
  145. data/modules/mu/clouds/cloudformation/server_pool.rb +1 -0
  146. data/modules/mu/clouds/cloudformation/vpc.rb +0 -2
  147. data/modules/mu/clouds/google.rb +554 -117
  148. data/modules/mu/clouds/google/bucket.rb +173 -32
  149. data/modules/mu/clouds/google/container_cluster.rb +1112 -157
  150. data/modules/mu/clouds/google/database.rb +24 -47
  151. data/modules/mu/clouds/google/firewall_rule.rb +344 -89
  152. data/modules/mu/clouds/google/folder.rb +156 -79
  153. data/modules/mu/clouds/google/group.rb +272 -82
  154. data/modules/mu/clouds/google/habitat.rb +177 -52
  155. data/modules/mu/clouds/google/loadbalancer.rb +9 -34
  156. data/modules/mu/clouds/google/role.rb +1211 -0
  157. data/modules/mu/clouds/google/server.rb +491 -227
  158. data/modules/mu/clouds/google/server_pool.rb +233 -48
  159. data/modules/mu/clouds/google/user.rb +479 -125
  160. data/modules/mu/clouds/google/userdata/linux.erb +3 -3
  161. data/modules/mu/clouds/google/userdata/windows.erb +9 -9
  162. data/modules/mu/clouds/google/vpc.rb +381 -223
  163. data/modules/mu/config.rb +689 -214
  164. data/modules/mu/config/bucket.rb +1 -1
  165. data/modules/mu/config/cache_cluster.rb +1 -1
  166. data/modules/mu/config/cache_cluster.yml +0 -4
  167. data/modules/mu/config/container_cluster.rb +18 -9
  168. data/modules/mu/config/database.rb +6 -23
  169. data/modules/mu/config/firewall_rule.rb +9 -15
  170. data/modules/mu/config/folder.rb +22 -21
  171. data/modules/mu/config/habitat.rb +22 -21
  172. data/modules/mu/config/loadbalancer.rb +2 -2
  173. data/modules/mu/config/role.rb +9 -40
  174. data/modules/mu/config/server.rb +26 -5
  175. data/modules/mu/config/server_pool.rb +1 -1
  176. data/modules/mu/config/storage_pool.rb +2 -2
  177. data/modules/mu/config/user.rb +4 -0
  178. data/modules/mu/config/vpc.rb +350 -110
  179. data/modules/mu/defaults/{amazon_images.yaml → AWS.yaml} +37 -39
  180. data/modules/mu/defaults/Azure.yaml +17 -0
  181. data/modules/mu/defaults/Google.yaml +24 -0
  182. data/modules/mu/defaults/README.md +1 -1
  183. data/modules/mu/deploy.rb +168 -125
  184. data/modules/mu/groomer.rb +2 -1
  185. data/modules/mu/groomers/ansible.rb +104 -32
  186. data/modules/mu/groomers/chef.rb +96 -44
  187. data/modules/mu/kittens.rb +20602 -0
  188. data/modules/mu/logger.rb +38 -11
  189. data/modules/mu/master.rb +90 -8
  190. data/modules/mu/master/chef.rb +2 -3
  191. data/modules/mu/master/ldap.rb +0 -1
  192. data/modules/mu/master/ssl.rb +250 -0
  193. data/modules/mu/mommacat.rb +917 -513
  194. data/modules/scratchpad.erb +1 -1
  195. data/modules/tests/super_complex_bok.yml +0 -0
  196. data/modules/tests/super_simple_bok.yml +0 -0
  197. data/roles/mu-master.json +2 -1
  198. data/spec/azure_creds +5 -0
  199. data/spec/mu.yaml +56 -0
  200. data/spec/mu/clouds/azure_spec.rb +164 -27
  201. data/spec/spec_helper.rb +5 -0
  202. data/test/clean_up.py +0 -0
  203. data/test/exec_inspec.py +0 -0
  204. data/test/exec_mu_install.py +0 -0
  205. data/test/exec_retry.py +0 -0
  206. data/test/smoke_test.rb +0 -0
  207. metadata +90 -118
  208. data/cookbooks/mu-jenkins/Berksfile +0 -14
  209. data/cookbooks/mu-jenkins/CHANGELOG.md +0 -13
  210. data/cookbooks/mu-jenkins/LICENSE +0 -37
  211. data/cookbooks/mu-jenkins/README.md +0 -105
  212. data/cookbooks/mu-jenkins/attributes/default.rb +0 -42
  213. data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +0 -73
  214. data/cookbooks/mu-jenkins/files/default/deploy_config.xml +0 -44
  215. data/cookbooks/mu-jenkins/metadata.rb +0 -21
  216. data/cookbooks/mu-jenkins/recipes/default.rb +0 -195
  217. data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +0 -54
  218. data/cookbooks/mu-jenkins/recipes/public_key.rb +0 -24
  219. data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +0 -24
  220. data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +0 -14
  221. data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +0 -6
  222. data/cookbooks/nagios/Berksfile +0 -11
  223. data/cookbooks/nagios/CHANGELOG.md +0 -589
  224. data/cookbooks/nagios/CONTRIBUTING.md +0 -11
  225. data/cookbooks/nagios/LICENSE +0 -37
  226. data/cookbooks/nagios/README.md +0 -328
  227. data/cookbooks/nagios/TESTING.md +0 -2
  228. data/cookbooks/nagios/attributes/config.rb +0 -171
  229. data/cookbooks/nagios/attributes/default.rb +0 -228
  230. data/cookbooks/nagios/chefignore +0 -102
  231. data/cookbooks/nagios/definitions/command.rb +0 -33
  232. data/cookbooks/nagios/definitions/contact.rb +0 -33
  233. data/cookbooks/nagios/definitions/contactgroup.rb +0 -33
  234. data/cookbooks/nagios/definitions/host.rb +0 -33
  235. data/cookbooks/nagios/definitions/hostdependency.rb +0 -33
  236. data/cookbooks/nagios/definitions/hostescalation.rb +0 -34
  237. data/cookbooks/nagios/definitions/hostgroup.rb +0 -33
  238. data/cookbooks/nagios/definitions/nagios_conf.rb +0 -38
  239. data/cookbooks/nagios/definitions/resource.rb +0 -33
  240. data/cookbooks/nagios/definitions/service.rb +0 -33
  241. data/cookbooks/nagios/definitions/servicedependency.rb +0 -33
  242. data/cookbooks/nagios/definitions/serviceescalation.rb +0 -34
  243. data/cookbooks/nagios/definitions/servicegroup.rb +0 -33
  244. data/cookbooks/nagios/definitions/timeperiod.rb +0 -33
  245. data/cookbooks/nagios/libraries/base.rb +0 -314
  246. data/cookbooks/nagios/libraries/command.rb +0 -91
  247. data/cookbooks/nagios/libraries/contact.rb +0 -230
  248. data/cookbooks/nagios/libraries/contactgroup.rb +0 -112
  249. data/cookbooks/nagios/libraries/custom_option.rb +0 -36
  250. data/cookbooks/nagios/libraries/data_bag_helper.rb +0 -23
  251. data/cookbooks/nagios/libraries/default.rb +0 -90
  252. data/cookbooks/nagios/libraries/host.rb +0 -412
  253. data/cookbooks/nagios/libraries/hostdependency.rb +0 -181
  254. data/cookbooks/nagios/libraries/hostescalation.rb +0 -173
  255. data/cookbooks/nagios/libraries/hostgroup.rb +0 -119
  256. data/cookbooks/nagios/libraries/nagios.rb +0 -282
  257. data/cookbooks/nagios/libraries/resource.rb +0 -59
  258. data/cookbooks/nagios/libraries/service.rb +0 -455
  259. data/cookbooks/nagios/libraries/servicedependency.rb +0 -215
  260. data/cookbooks/nagios/libraries/serviceescalation.rb +0 -195
  261. data/cookbooks/nagios/libraries/servicegroup.rb +0 -144
  262. data/cookbooks/nagios/libraries/timeperiod.rb +0 -160
  263. data/cookbooks/nagios/libraries/users_helper.rb +0 -54
  264. data/cookbooks/nagios/metadata.rb +0 -25
  265. data/cookbooks/nagios/recipes/_load_databag_config.rb +0 -153
  266. data/cookbooks/nagios/recipes/_load_default_config.rb +0 -241
  267. data/cookbooks/nagios/recipes/apache.rb +0 -48
  268. data/cookbooks/nagios/recipes/default.rb +0 -204
  269. data/cookbooks/nagios/recipes/nginx.rb +0 -82
  270. data/cookbooks/nagios/recipes/pagerduty.rb +0 -143
  271. data/cookbooks/nagios/recipes/server_package.rb +0 -40
  272. data/cookbooks/nagios/recipes/server_source.rb +0 -164
  273. data/cookbooks/nagios/templates/default/apache2.conf.erb +0 -96
  274. data/cookbooks/nagios/templates/default/cgi.cfg.erb +0 -266
  275. data/cookbooks/nagios/templates/default/commands.cfg.erb +0 -13
  276. data/cookbooks/nagios/templates/default/contacts.cfg.erb +0 -37
  277. data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +0 -25
  278. data/cookbooks/nagios/templates/default/hosts.cfg.erb +0 -15
  279. data/cookbooks/nagios/templates/default/htpasswd.users.erb +0 -6
  280. data/cookbooks/nagios/templates/default/nagios.cfg.erb +0 -22
  281. data/cookbooks/nagios/templates/default/nginx.conf.erb +0 -62
  282. data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +0 -185
  283. data/cookbooks/nagios/templates/default/resource.cfg.erb +0 -27
  284. data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +0 -15
  285. data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +0 -14
  286. data/cookbooks/nagios/templates/default/services.cfg.erb +0 -14
  287. data/cookbooks/nagios/templates/default/templates.cfg.erb +0 -31
  288. data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +0 -13
  289. data/extras/image-generators/aws/centos6.yaml +0 -18
  290. data/modules/mu/defaults/google_images.yaml +0 -16
  291. data/roles/mu-master-jenkins.json +0 -24
@@ -17,47 +17,28 @@ module MU
17
17
  class Google
18
18
  # A database as configured in {MU::Config::BasketofKittens::databases}
19
19
  class Database < MU::Cloud::Database
20
- @deploy = nil
21
- @project_id = nil
22
- @config = nil
23
- attr_reader :mu_name
24
- attr_reader :cloud_id
25
- attr_reader :config
26
- attr_reader :groomer
27
-
28
- # @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
29
- # @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::databases}
30
- def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
31
- @deploy = mommacat
32
- @config = MU::Config.manxify(kitten_cfg)
33
- @cloud_id ||= cloud_id
34
- # @mu_name = mu_name ? mu_name : @deploy.getResourceName(@config["name"])
20
+
21
+ # Initialize this cloud resource object. Calling +super+ will invoke the initializer defined under {MU::Cloud}, which should set the attribtues listed in {MU::Cloud::PUBLIC_ATTRS} as well as applicable dependency shortcuts, like <tt>@vpc</tt>, for us.
22
+ # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
23
+ def initialize(**args)
24
+ super
35
25
  @config["groomer"] = MU::Config.defaultGroomer unless @config["groomer"]
36
26
  @groomclass = MU::Groomer.loadGroomer(@config["groomer"])
37
27
 
38
- if !mu_name.nil?
39
- @mu_name = mu_name
40
- @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
41
- if !@project_id
42
- project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
43
- @project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
28
+ @mu_name ||=
29
+ if @config and @config['engine'] and @config["engine"].match(/^sqlserver/)
30
+ @deploy.getResourceName(@config["name"], max_length: 15)
31
+ else
32
+ @deploy.getResourceName(@config["name"], max_length: 63)
44
33
  end
45
- else
46
- @mu_name ||=
47
- if @config and @config['engine'] and @config["engine"].match(/^sqlserver/)
48
- @deploy.getResourceName(@config["name"], max_length: 15)
49
- else
50
- @deploy.getResourceName(@config["name"], max_length: 63)
51
- end
52
-
53
- @mu_name.gsub(/(--|-$)/i, "").gsub(/(_)/, "-").gsub!(/^[^a-z]/i, "")
54
- end
34
+
35
+ @mu_name.gsub(/(--|-$)/i, "").gsub(/(_)/, "-").gsub!(/^[^a-z]/i, "")
55
36
  end
56
37
 
57
38
  # Called automatically by {MU::Deploy#createResources}
58
39
  # @return [String]: The cloud provider's identifier for this database instance.
59
40
  def create
60
- @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
41
+ @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloud_id
61
42
  labels = {}
62
43
  MU::MommaCat.listStandardTags.each_pair { |name, value|
63
44
  if !value.nil?
@@ -85,19 +66,15 @@ module MU
85
66
  end
86
67
 
87
68
  # Locate an existing Database or Databases and return an array containing matching GCP resource descriptors for those that match.
88
- # @param cloud_id [String]: The cloud provider's identifier for this resource.
89
- # @param region [String]: The cloud provider region
90
- # @param tag_key [String]: A tag key to search.
91
- # @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
92
- # @param flags [Hash]: Optional flags
93
69
  # @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching Databases
94
- def self.find(cloud_id: nil, region: MU.curRegion, tag_key: "Name", tag_value: nil, flags: {}, credentials: nil)
95
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
70
+ def self.find(**args)
71
+ args[:project] ||= args[:habitat]
72
+ args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
96
73
  end
97
74
 
98
75
  # Called automatically by {MU::Deploy#createResources}
99
76
  def groom
100
- @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
77
+ @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloud_id
101
78
  end
102
79
 
103
80
  # Register a description of this database instance with this deployment's metadata.
@@ -134,13 +111,13 @@ module MU
134
111
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
135
112
  flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
136
113
  skipsnapshots||= flags["skipsnapshots"]
137
- instances = MU::Cloud::Google.sql(credentials: credentials).list_instances(flags['project'], filter: %Q{userLabels.mu-id:"#{MU.deploy_id.downcase}"})
138
- if instances and instances.items
139
- instances.items.each { |instance|
140
- MU.log "Deleting Cloud SQL instance #{instance.name}"
141
- MU::Cloud::Google.sql(credentials: credentials).delete_instance(flags['project'], instance.name) if !noop
142
- }
143
- end
114
+ # instances = MU::Cloud::Google.sql(credentials: credentials).list_instances(flags['project'], filter: %Q{userLabels.mu-id:"#{MU.deploy_id.downcase}"})
115
+ # if instances and instances.items
116
+ # instances.items.each { |instance|
117
+ # MU.log "Deleting Cloud SQL instance #{instance.name}"
118
+ # MU::Cloud::Google.sql(credentials: credentials).delete_instance(flags['project'], instance.name) if !noop
119
+ # }
120
+ # end
144
121
  end
145
122
 
146
123
  # Cloud-specific configuration properties.
@@ -18,57 +18,49 @@ module MU
18
18
  class Google
19
19
  # A firewall ruleset as configured in {MU::Config::BasketofKittens::firewall_rules}
20
20
  class FirewallRule < MU::Cloud::FirewallRule
21
-
22
- @deploy = nil
23
- @config = nil
24
- @project_id = nil
25
21
  @admin_sgs = Hash.new
26
22
  @admin_sg_semaphore = Mutex.new
27
23
 
28
- attr_reader :mu_name
29
- attr_reader :config
30
- attr_reader :cloud_id
31
-
32
- # @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
33
- # @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::firewall_rules}
34
- def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
35
- @deploy = mommacat
36
- @config = MU::Config.manxify(kitten_cfg)
37
- @cloud_id ||= cloud_id
38
- if !mu_name.nil?
39
- @mu_name = mu_name
40
- # This is really a placeholder, since we "own" multiple rule sets
41
- @cloud_id ||= MU::Cloud::Google.nameStr(@mu_name+"-ingress-allow")
42
- @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
43
- if !@project_id
44
- project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
45
- @project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
46
- end
24
+ # Firewall protocols supported by GCP as of early 2019
25
+ PROTOS = ["udp", "tcp", "icmp", "esp", "ah", "sctp", "ipip"]
26
+
27
+ # Our default subset of supported firewall protocols
28
+ STD_PROTOS = ["icmp", "tcp", "udp"]
29
+
30
+ # Initialize this cloud resource object. Calling +super+ will invoke the initializer defined under {MU::Cloud}, which should set the attribtues listed in {MU::Cloud::PUBLIC_ATTRS} as well as applicable dependency shortcuts, like <tt>@vpc</tt>, for us.
31
+ # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
32
+ def initialize(**args)
33
+ super
34
+
35
+ if !@vpc.nil?
36
+ @mu_name ||= @deploy.getResourceName(@config['name'], need_unique_string: true, max_length: 61)
47
37
  else
48
- if !@vpc.nil?
49
- @mu_name = @deploy.getResourceName(@config['name'], need_unique_string: true, max_length: 61)
50
- else
51
- @mu_name = @deploy.getResourceName(@config['name'], max_length: 61)
52
- end
38
+ @mu_name ||= @deploy.getResourceName(@config['name'], max_length: 61)
53
39
  end
54
-
55
40
  end
56
41
 
57
42
  attr_reader :rulesets
58
43
 
59
44
  # Called by {MU::Deploy#createResources}
60
45
  def create
61
- @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
46
+ @cloud_id = @mu_name.downcase.gsub(/[^-a-z0-9]/, "-")
62
47
 
63
- vpc_id = @vpc.cloudobj.url if !@vpc.nil? and !@vpc.cloudobj.nil?
48
+ vpc_id = @vpc.url if !@vpc.nil?
64
49
  vpc_id ||= @config['vpc']['vpc_id'] if @config['vpc'] and @config['vpc']['vpc_id']
65
50
 
66
- allrules = {}
67
- # The set of rules might actually compose into multiple firewall
68
- # objects, so figure that out.
51
+ if vpc_id.nil?
52
+ raise MuError, "Failed to resolve VPC for #{self}"
53
+ end
54
+
55
+ params = {
56
+ :name => @cloud_id,
57
+ :network => vpc_id
58
+ }
59
+
69
60
  @config['rules'].each { |rule|
70
61
  srcs = []
71
62
  ruleobj = nil
63
+ # XXX 'all' and 'standard' keywords
72
64
  if ["tcp", "udp"].include?(rule['proto']) and (rule['port_range'] or rule['port'])
73
65
  ruleobj = MU::Cloud::Google.compute(:Firewall)::Allowed.new(
74
66
  ip_protocol: rule['proto'],
@@ -83,55 +75,59 @@ module MU
83
75
  rule['hosts'].each { |cidr| srcs << cidr }
84
76
  end
85
77
 
86
- ["ingress", "egress"].each { |dir|
87
- if rule[dir] or (dir == "ingress" and !rule.has_key?("egress"))
88
- setname = @deploy.getResourceName(@mu_name+"-"+dir+"-"+(rule['deny'] ? "deny" : "allow"), max_length: 61).downcase
89
- @cloud_id ||= setname
90
- allrules[setname] ||= {
91
- :name => setname,
92
- :direction => dir.upcase,
93
- :network => vpc_id
94
- }
95
- if @deploy
96
- allrules[setname][:description] = @deploy.deploy_id
97
- end
98
- ['source_service_accounts', 'source_tags', 'target_tags', 'target_service_accounts'].each { |filter|
99
- if config[filter] and config[filter].size > 0
100
- allrules[setname][filter.to_sym] = config[filter].dup
101
- end
102
- }
103
- action = rule['deny'] ? :denied : :allowed
104
- allrules[setname][action] ||= []
105
- allrules[setname][action] << ruleobj
106
- ipparam = dir == "ingress" ? :source_ranges : :destination_ranges
107
- allrules[setname][ipparam] ||= []
108
- allrules[setname][ipparam].concat(srcs)
109
- allrules[setname][:priority] = rule['weight'] if rule['weight']
110
- end
111
- }
112
- }
78
+ dir = (rule["ingress"] or !rule["egress"]) ? "INGRESS" : "EGRESS"
79
+ if params[:direction] and params[:direction] != dir
80
+ MU.log "Google Cloud firewalls cannot mix ingress and egress rules", MU::ERR, details: @config['rules']
81
+ raise MuError, "Google Cloud firewalls cannot mix ingress and egress rules"
82
+ end
113
83
 
114
- parent_thread_id = Thread.current.object_id
115
- threads = []
84
+ params[:direction] = dir
116
85
 
117
- allrules.each_value { |fwdesc|
118
- threads << Thread.new {
119
- fwobj = MU::Cloud::Google.compute(:Firewall).new(fwdesc)
120
- MU.log "Creating firewall #{fwdesc[:name]} in project #{@project_id}", details: fwobj
121
- resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_firewall(@project_id, fwobj)
122
- # XXX Check for empty (no hosts) sets
123
- # MU.log "Can't create empty firewalls in Google Cloud, skipping #{@mu_name}", MU::WARN
86
+ if @deploy
87
+ params[:description] = @deploy.deploy_id
88
+ end
89
+ filters = if dir == "INGRESS"
90
+ ['source_service_accounts', 'source_tags']
91
+ else
92
+ ['target_service_accounts', 'target_tags']
93
+ end
94
+ filters.each { |filter|
95
+ if config[filter] and config[filter].size > 0
96
+ params[filter.to_sym] = config[filter].dup
97
+ end
124
98
  }
99
+ action = rule['deny'] ? :denied : :allowed
100
+ params[action] ||= []
101
+ params[action] << ruleobj
102
+ ipparam = dir == "INGRESS" ? :source_ranges : :destination_ranges
103
+ params[ipparam] ||= []
104
+ params[ipparam].concat(srcs)
105
+ params[:priority] = rule['weight'] if rule['weight']
125
106
  }
126
107
 
127
- threads.each do |t|
128
- t.join
129
- end
108
+ fwobj = MU::Cloud::Google.compute(:Firewall).new(params)
109
+ MU.log "Creating firewall #{@cloud_id} in project #{@project_id}", details: fwobj
110
+ begin
111
+ MU::Cloud::Google.compute(credentials: @config['credentials']).insert_firewall(@project_id, fwobj)
112
+ rescue ::Google::Apis::ClientError => e
113
+ MU.log @config['project']+"/"+@config['name']+": "+@cloud_id, MU::ERR, details: @config['vpc']
114
+ MU.log e.inspect, MU::ERR, details: fwobj
115
+ if e.message.match(/Invalid value for field/)
116
+ dependencies(use_cache: false, debug: true)
117
+ end
118
+ raise e
119
+ end
120
+ # Make sure it actually got made before we move on
121
+ desc = nil
122
+ begin
123
+ desc = MU::Cloud::Google.compute(credentials: @config['credentials']).get_firewall(@project_id, @cloud_id)
124
+ sleep 1
125
+ end while desc.nil?
126
+ desc
130
127
  end
131
128
 
132
129
  # Called by {MU::Deploy#createResources}
133
130
  def groom
134
- @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
135
131
  end
136
132
 
137
133
  # Log metadata about this ruleset to the currently running deployment
@@ -141,7 +137,7 @@ module MU
141
137
  )
142
138
  sg_data ||= {}
143
139
  sg_data["group_id"] = @cloud_id
144
- sg_data["project_id"] = @project_id
140
+ sg_data["project_id"] = habitat_id
145
141
  sg_data["cloud_id"] = @cloud_id
146
142
 
147
143
  return sg_data
@@ -158,24 +154,31 @@ module MU
158
154
  def addRule(hosts, proto: "tcp", port: nil, egress: false, port_range: "0-65535")
159
155
  end
160
156
 
161
- # Locate an existing security group or groups and return an array containing matching AWS resource descriptors for those that match.
162
- # @param cloud_id [String]: The cloud provider's identifier for this resource.
163
- # @param region [String]: The cloud provider region
164
- # @param tag_key [String]: A tag key to search.
165
- # @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
166
- # @param flags [Hash]: Optional flags
167
- # @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching FirewallRules
168
- def self.find(cloud_id: nil, region: MU.curRegion, tag_key: "Name", tag_value: nil, flags: {}, credentials: nil)
169
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
157
+ # Locate and return cloud provider descriptors of this resource type
158
+ # which match the provided parameters, or all visible resources if no
159
+ # filters are specified. At minimum, implementations of +find+ must
160
+ # honor +credentials+ and +cloud_id+ arguments. We may optionally
161
+ # support other search methods, such as +tag_key+ and +tag_value+, or
162
+ # cloud-specific arguments like +project+. See also {MU::MommaCat.findStray}.
163
+ # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
164
+ # @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching resources
165
+ def self.find(**args)
166
+ args[:project] ||= args[:habitat]
167
+ args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
170
168
 
171
169
  found = {}
172
- resp = MU::Cloud::Google.compute(credentials: credentials).list_firewalls(flags["project"])
170
+ resp = begin
171
+ MU::Cloud::Google.compute(credentials: args[:credentials]).list_firewalls(args[:project])
172
+ rescue ::Google::Apis::ClientError => e
173
+ raise e if !e.message.match(/^(?:notFound|forbidden): /)
174
+ end
173
175
  if resp and resp.items
174
176
  resp.items.each { |fw|
175
- next if !cloud_id.nil? and fw.name != cloud_id
177
+ next if !args[:cloud_id].nil? and fw.name != args[:cloud_id]
176
178
  found[fw.name] = fw
177
179
  }
178
180
  end
181
+
179
182
  found
180
183
  end
181
184
 
@@ -199,6 +202,8 @@ module MU
199
202
  # @return [void]
200
203
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
201
204
  flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
205
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
206
+
202
207
  MU::Cloud::Google.compute(credentials: credentials).delete(
203
208
  "firewall",
204
209
  flags["project"],
@@ -207,18 +212,167 @@ module MU
207
212
  )
208
213
  end
209
214
 
215
+ # Reverse-map our cloud description into a runnable config hash.
216
+ # We assume that any values we have in +@config+ are placeholders, and
217
+ # calculate our own accordingly based on what's live in the cloud.
218
+ def toKitten(rootparent: nil, billing: nil, habitats: nil)
219
+
220
+ if cloud_desc.name.match(/^[a-f0-9]+$/)
221
+ gke_ish = true
222
+ cloud_desc.target_tags.each { |tag|
223
+ gke_ish = false if !tag.match(/^gke-/)
224
+ }
225
+ if gke_ish
226
+ MU.log "FirewallRule #{cloud_desc.name} appears to belong to a ContainerCluster, skipping adoption", MU::DEBUG
227
+ return nil
228
+ end
229
+ end
230
+
231
+ bok = {
232
+ "cloud" => "Google",
233
+ "project" => @config['project'],
234
+ "credentials" => @config['credentials']
235
+ }
236
+
237
+ bok['rules'] = []
238
+ bok['name'] = cloud_desc.name.dup
239
+ bok['cloud_id'] = cloud_desc.name.dup
240
+
241
+
242
+ cloud_desc.network.match(/(?:^|\/)projects\/(.*?)\/.*?\/networks\/([^\/]+)(?:$|\/)/)
243
+ vpc_proj = Regexp.last_match[1]
244
+ vpc_id = Regexp.last_match[2]
245
+
246
+ if vpc_id == "default" and !@config['project']
247
+ raise MuError, "FirewallRule toKitten: I'm in 'default' VPC but can't figure out what project I'm in"
248
+ end
249
+
250
+ # XXX make sure this is sane (that these rules come with default VPCs)
251
+ if vpc_id == "default" and ["default-allow-icmp", "default-allow-http"].include?(cloud_desc.name)
252
+ return nil
253
+ end
254
+
255
+ if vpc_id != "default"
256
+ bok['vpc'] = MU::Config::Ref.get(
257
+ id: vpc_id,
258
+ habitat: MU::Config::Ref.get(
259
+ id: vpc_proj,
260
+ cloud: "Google",
261
+ credentials: @credentials,
262
+ type: "habitats"
263
+ ),
264
+ cloud: "Google",
265
+ credentials: @config['credentials'],
266
+ type: "vpcs"
267
+ )
268
+ end
269
+
270
+ byport = {}
271
+
272
+ rule_list = []
273
+ is_deny = false
274
+ if cloud_desc.denied
275
+ rule_list = cloud_desc.denied
276
+ is_deny = true
277
+ else
278
+ rule_list = cloud_desc.allowed
279
+ end
280
+
281
+ rule_list.each { |rule|
282
+ hosts = if cloud_desc.direction == "INGRESS"
283
+ cloud_desc.source_ranges ? cloud_desc.source_ranges : ["0.0.0.0/0"]
284
+ else
285
+ cloud_desc.destination_ranges ? cloud_desc.destination_ranges : ["0.0.0.0/0"]
286
+ end
287
+ hosts.map! { |h|
288
+ h = h+"/32" if h.match(/^\d+\.\d+\.\d+\.\d+$/)
289
+ h
290
+ }
291
+ proto = rule.ip_protocol ? rule.ip_protocol : "all"
292
+
293
+ if rule.ports
294
+ rule.ports.each { |ports|
295
+ ports = "0-65535" if ["1-65535", "1-65536", "0-65536"].include?(ports)
296
+ byport[ports] ||= {}
297
+ byport[ports][hosts] ||= []
298
+ byport[ports][hosts] << proto
299
+ }
300
+ else
301
+ byport["0-65535"] ||= {}
302
+ byport["0-65535"][hosts] ||= []
303
+ byport["0-65535"][hosts] << proto
304
+ end
305
+
306
+ }
307
+
308
+ byport.each_pair { |ports, hostlists|
309
+ hostlists.each_pair { |hostlist, protos|
310
+ protolist = if protos.sort.uniq == PROTOS.sort.uniq
311
+ ["all"]
312
+ elsif protos.sort.uniq == ["icmp", "tcp", "udp"]
313
+ ["standard"]
314
+ else
315
+ protos
316
+ end
317
+ protolist.each { |proto|
318
+ rule = {
319
+ "proto" => proto,
320
+ "hosts" => hostlist
321
+ }
322
+ rule["deny"] = true if is_deny
323
+ if cloud_desc.priority and cloud_desc.priority != 1000
324
+ rule["weight"] = cloud_desc.priority
325
+ end
326
+ if ports.match(/-/)
327
+ rule["port_range"] = ports
328
+ else
329
+ rule["port"] = ports.to_i
330
+ end
331
+ if cloud_desc.source_service_accounts
332
+ rule["source_service_accounts"] = cloud_desc.source_service_accounts
333
+ end
334
+ if cloud_desc.source_tags
335
+ rule["source_tags"] = cloud_desc.source_tags
336
+ end
337
+ if cloud_desc.target_service_accounts
338
+ rule["target_service_accounts"] = cloud_desc.target_service_accounts
339
+ end
340
+ if cloud_desc.target_tags
341
+ rule["target_tags"] = cloud_desc.target_tags
342
+ end
343
+ if cloud_desc.direction == "EGRESS"
344
+ rule['egress'] = true
345
+ rule['ingress'] = false
346
+ end
347
+ bok['rules'] << rule
348
+ }
349
+ }
350
+ }
351
+
352
+ bok
353
+ end
354
+
210
355
  # Cloud-specific configuration properties.
211
356
  # @param config [MU::Config]: The calling MU::Config object
212
357
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
213
- def self.schema(config)
358
+ def self.schema(config = nil)
214
359
  toplevel_required = []
215
- # ['source_ranges', 'source_service_accounts', 'source_tags', 'target_ranges', 'target_service_accounts'].each { |filter|
216
360
  schema = {
217
361
  "rules" => {
218
362
  "items" => {
219
363
  "properties" => {
364
+ "weight" => {
365
+ "type" => "integer",
366
+ "description" => "Explicitly set a priority for this firewall rule, between 0 and 65535, with lower numbered priority rules having greater precedence."
367
+ },
368
+ "deny" => {
369
+ "type" => "boolean",
370
+ "default" => false,
371
+ "description" => "Set this rule to +DENY+ traffic instead of +ALLOW+"
372
+ },
220
373
  "proto" => {
221
- "enum" => ["udp", "tcp", "icmp", "all"]
374
+ "description" => "The protocol to allow with this rule. The +standard+ keyword will expand to a series of identical rules covering +icmp+, +tcp+, and +udp; the +all+ keyword will expand to a series of identical rules for all supported protocols.",
375
+ "enum" => PROTOS + ["all", "standard"]
222
376
  },
223
377
  "source_tags" => {
224
378
  "type" => "array",
@@ -265,6 +419,107 @@ module MU
265
419
  # @return [Boolean]: True if validation succeeded, False otherwise
266
420
  def self.validateConfig(acl, config)
267
421
  ok = true
422
+ acl['project'] ||= MU::Cloud::Google.defaultProject(acl['credentials'])
423
+
424
+ if acl['vpc']
425
+ acl['vpc']['project'] ||= acl['project']
426
+ end
427
+
428
+ acl['rules'] ||= []
429
+
430
+ # Firewall entries without rules are illegal in GCP, so insert a
431
+ # default-deny placeholder.
432
+ if acl['rules'].empty?
433
+ acl['rules'] << {
434
+ "deny" => true,
435
+ "proto" => "all",
436
+ "hosts" => ["0.0.0.0/0"],
437
+ "weight" => 65535
438
+ }
439
+ end
440
+
441
+ # First, expand some of our protocol shorthand into a real list
442
+ append = []
443
+ delete = []
444
+ acl['rules'].each { |r|
445
+ if !r['egress']
446
+ if !r['source_tags'] and !r['source_service_accounts'] and
447
+ (!r['hosts'] or r['hosts'].empty?)
448
+ r['hosts'] = ['0.0.0.0/0']
449
+ end
450
+ else
451
+ if !r['destination_tags'] and !r['destination_service_accounts'] and
452
+ (!r['hosts'] or r['hosts'].empty?)
453
+ r['hosts'] = ['0.0.0.0/0']
454
+ end
455
+ end
456
+
457
+ if r['proto'] == "standard"
458
+ STD_PROTOS.each { |p|
459
+ newrule = r.dup
460
+ newrule['proto'] = p
461
+ append << newrule
462
+ }
463
+ delete << r
464
+ elsif r['proto'] == "all"
465
+ PROTOS.each { |p|
466
+ newrule = r.dup
467
+ newrule['proto'] = p
468
+ append << newrule
469
+ }
470
+ delete << r
471
+ end
472
+
473
+ }
474
+ delete.each { |r|
475
+ acl['rules'].delete(r)
476
+ }
477
+ acl['rules'].concat(append)
478
+
479
+ # Next, bucket these by what combination of allow/deny and
480
+ # ingress/egress rule they are. If we have more than one
481
+ # classification
482
+ rules_by_class = {
483
+ "allow-ingress" => [],
484
+ "allow-egress" => [],
485
+ "deny-ingress" => [],
486
+ "deny-egress" => [],
487
+ }
488
+
489
+ acl['rules'].each { |rule|
490
+ if rule['deny']
491
+ if rule['egress']
492
+ rules_by_class["deny-egress"] << rule
493
+ else
494
+ rules_by_class["deny-ingress"] << rule
495
+ end
496
+ else
497
+ if rule['egress']
498
+ rules_by_class["allow-egress"] << rule
499
+ else
500
+ rules_by_class["allow-ingress"] << rule
501
+ end
502
+ end
503
+ }
504
+
505
+ rules_by_class.reject! { |k, v| v.size == 0 }
506
+
507
+ # Generate other firewall rule objects to cover the other behaviors
508
+ # we've requested, if indeed we've done so.
509
+ if rules_by_class.size > 1
510
+ keep = rules_by_class.keys.first
511
+ acl['rules'] = rules_by_class[keep]
512
+ rules_by_class.delete(keep)
513
+ rules_by_class.each_pair { |behaviors, rules|
514
+ newrule = acl.dup
515
+ newrule['name'] += "-"+behaviors
516
+ newrule['rules'] = rules
517
+ ok = false if !config.insertKitten(newrule, "firewall_rules")
518
+
519
+ }
520
+ end
521
+
522
+ ok
268
523
  end
269
524
 
270
525
  private