cloud-mu 2.1.0beta → 3.0.0beta

Sign up to get free protection for your applications and to get access to all the features.
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