fhcap-cli 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rakeTasks +7 -0
  4. data/.rspec +1 -0
  5. data/CHANGELOG.md +15 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +150 -0
  9. data/Rakefile +2 -0
  10. data/bin/fhcap +6 -0
  11. data/fhcap-cli.gemspec +44 -0
  12. data/lib/cookbooks/provision/libraries/provision.rb +140 -0
  13. data/lib/cookbooks/provision/metadata.rb +7 -0
  14. data/lib/cookbooks/provision/recipes/aws.rb +15 -0
  15. data/lib/cookbooks/provision/recipes/aws_cluster_create.rb +59 -0
  16. data/lib/cookbooks/provision/recipes/aws_cluster_create_elb.rb +61 -0
  17. data/lib/cookbooks/provision/recipes/aws_cluster_destroy.rb +52 -0
  18. data/lib/cookbooks/provision/recipes/cluster_create.rb +2 -0
  19. data/lib/cookbooks/provision/recipes/cluster_destroy.rb +2 -0
  20. data/lib/cookbooks/provision/recipes/cluster_destroy_instances.rb +11 -0
  21. data/lib/cookbooks/provision/recipes/cluster_provision.rb +4 -0
  22. data/lib/cookbooks/provision/recipes/cluster_provision_instances.rb +55 -0
  23. data/lib/cookbooks/provision/recipes/cluster_status.rb +24 -0
  24. data/lib/cookbooks/provision/recipes/common.rb +9 -0
  25. data/lib/cookbooks/provision/recipes/default.rb +5 -0
  26. data/lib/cookbooks/provision/recipes/openstack.rb +11 -0
  27. data/lib/cookbooks/provision/recipes/openstack_cluster_create.rb +11 -0
  28. data/lib/cookbooks/provision/recipes/openstack_cluster_destroy.rb +4 -0
  29. data/lib/cookbooks/provision/recipes/reset_rabbitmq.rb +49 -0
  30. data/lib/cookbooks/provision/recipes/restart_services.rb +24 -0
  31. data/lib/extensions/chef/provisioning.rb +21 -0
  32. data/lib/extensions/chef/provisioning/aws_driver/driver.rb +46 -0
  33. data/lib/extensions/chef/provisioning/chef_run_data.rb +18 -0
  34. data/lib/extensions/cheffish/merged_config.rb +9 -0
  35. data/lib/fhcap.rb +14 -0
  36. data/lib/fhcap/chef-dk/chef_runner.rb +94 -0
  37. data/lib/fhcap/cli.rb +75 -0
  38. data/lib/fhcap/cluster.rb +112 -0
  39. data/lib/fhcap/config.rb +104 -0
  40. data/lib/fhcap/cookbook.rb +75 -0
  41. data/lib/fhcap/dummy_node.rb +80 -0
  42. data/lib/fhcap/fhcap_helper.rb +9 -0
  43. data/lib/fhcap/kitchen.rb +235 -0
  44. data/lib/fhcap/knife.rb +74 -0
  45. data/lib/fhcap/knife_helper.rb +38 -0
  46. data/lib/fhcap/misc.rb +103 -0
  47. data/lib/fhcap/provider.rb +41 -0
  48. data/lib/fhcap/providers_helper.rb +60 -0
  49. data/lib/fhcap/repo.rb +52 -0
  50. data/lib/fhcap/repos_helper.rb +217 -0
  51. data/lib/fhcap/tasks/chef/chef_task_base.rb +82 -0
  52. data/lib/fhcap/tasks/chef/cookbook/list.rb +37 -0
  53. data/lib/fhcap/tasks/chef/cookbook/update_changelog.rb +63 -0
  54. data/lib/fhcap/tasks/chef/cookbook/update_metadata.rb +57 -0
  55. data/lib/fhcap/tasks/chef/cookbook/update_readme.rb +30 -0
  56. data/lib/fhcap/tasks/chef/cookbook/update_version.rb +90 -0
  57. data/lib/fhcap/tasks/chef/environments/create.rb +115 -0
  58. data/lib/fhcap/tasks/chef/environments/destroy.rb +37 -0
  59. data/lib/fhcap/tasks/chef/environments/promote_cookbooks.rb +47 -0
  60. data/lib/fhcap/tasks/chef/provisioning/chef_provisioning_task.rb +27 -0
  61. data/lib/fhcap/tasks/chef/provisioning/chef_provisioning_task_base.rb +38 -0
  62. data/lib/fhcap/tasks/chef/provisioning/create.rb +22 -0
  63. data/lib/fhcap/tasks/chef/provisioning/destroy.rb +21 -0
  64. data/lib/fhcap/tasks/chef/provisioning/provision.rb +19 -0
  65. data/lib/fhcap/tasks/chef/server/bootstrap.rb +165 -0
  66. data/lib/fhcap/tasks/chef/server/create_user.rb +97 -0
  67. data/lib/fhcap/tasks/chef/server/info.rb +82 -0
  68. data/lib/fhcap/tasks/chef/server/provision.rb +45 -0
  69. data/lib/fhcap/tasks/clean.rb +34 -0
  70. data/lib/fhcap/tasks/cluster/cluster_task_base.rb +57 -0
  71. data/lib/fhcap/tasks/cluster/create.rb +243 -0
  72. data/lib/fhcap/tasks/cluster/create_environment.rb +171 -0
  73. data/lib/fhcap/tasks/cluster/destroy.rb +30 -0
  74. data/lib/fhcap/tasks/cluster/destroy_environment.rb +28 -0
  75. data/lib/fhcap/tasks/cluster/info.rb +67 -0
  76. data/lib/fhcap/tasks/cluster/list.rb +40 -0
  77. data/lib/fhcap/tasks/cluster/provision.rb +46 -0
  78. data/lib/fhcap/tasks/cluster/status.rb +17 -0
  79. data/lib/fhcap/tasks/cluster/test.rb +15 -0
  80. data/lib/fhcap/tasks/knife/add.rb +111 -0
  81. data/lib/fhcap/tasks/knife/list.rb +22 -0
  82. data/lib/fhcap/tasks/knife/remove.rb +39 -0
  83. data/lib/fhcap/tasks/misc/create_dns_record.rb +100 -0
  84. data/lib/fhcap/tasks/misc/create_ssl_cert.rb +82 -0
  85. data/lib/fhcap/tasks/provider/add.rb +136 -0
  86. data/lib/fhcap/tasks/provider/list.rb +31 -0
  87. data/lib/fhcap/tasks/provider/remove.rb +28 -0
  88. data/lib/fhcap/tasks/repo/add.rb +57 -0
  89. data/lib/fhcap/tasks/repo/checkout.rb +144 -0
  90. data/lib/fhcap/tasks/repo/list.rb +22 -0
  91. data/lib/fhcap/tasks/repo/remove.rb +34 -0
  92. data/lib/fhcap/tasks/setup.rb +59 -0
  93. data/lib/fhcap/tasks/task_base.rb +89 -0
  94. data/lib/fhcap/thor_base.rb +121 -0
  95. data/lib/fhcap/version.rb +3 -0
  96. data/spec/fhcap/cli_spec.rb +6 -0
  97. data/spec/fhcap/tasks/cluster/create_spec.rb +46 -0
  98. data/spec/fhcap/tasks/knife/add_spec.rb +35 -0
  99. data/spec/fhcap/tasks/knife/remove_spec.rb +25 -0
  100. data/spec/fhcap/tasks/provider/add_spec.rb +61 -0
  101. data/spec/fhcap/tasks/provider/remove_spec.rb +25 -0
  102. data/spec/fhcap/tasks/repo/add_spec.rb +32 -0
  103. data/spec/fhcap/tasks/repo/remove_spec.rb +25 -0
  104. data/spec/fhcap/tasks/task_base_spec.rb +51 -0
  105. data/spec/fhcap/thor_base_spec.rb +9 -0
  106. data/spec/spec_helper.rb +23 -0
  107. data/spec/support/dummy_config.rb +7 -0
  108. data/spec/support/dummy_thor.rb +3 -0
  109. data/templates/chef/cookbook/changelog.md.erb +12 -0
  110. data/templates/chef/cookbook/metadata.erb +45 -0
  111. data/templates/chef/environment_core.json.erb +278 -0
  112. data/templates/chef/environment_empty.json.erb +10 -0
  113. data/templates/chef/environment_mbaas.json.erb +120 -0
  114. data/templates/chef/environment_single.json.erb +300 -0
  115. data/templates/cluster/aws/common.json.erb +43 -0
  116. data/templates/cluster/aws/core-3node.json.erb +106 -0
  117. data/templates/cluster/aws/core-small-9node.json.erb +333 -0
  118. data/templates/cluster/aws/mbaas-3node.json.erb +116 -0
  119. data/templates/cluster/aws/nginx-test.json.erb +93 -0
  120. data/templates/cluster/aws/single-blank.json.erb +41 -0
  121. data/templates/cluster/aws/single.json.erb +88 -0
  122. data/templates/cluster/core-3node.json.erb +8 -0
  123. data/templates/cluster/core-mbaas-6node.json.erb +13 -0
  124. data/templates/cluster/core-small-9node.json.erb +8 -0
  125. data/templates/cluster/mbaas-3node.json.erb +9 -0
  126. data/templates/cluster/nginx-test.json.erb +8 -0
  127. data/templates/cluster/openstack/common.json.erb +7 -0
  128. data/templates/cluster/openstack/core-3node.json.erb +14 -0
  129. data/templates/cluster/openstack/core-small-9node.json.erb +32 -0
  130. data/templates/cluster/openstack/mbaas-3node.json.erb +14 -0
  131. data/templates/cluster/openstack/nginx-test.json.erb +11 -0
  132. data/templates/cluster/openstack/single-blank.json.erb +10 -0
  133. data/templates/cluster/openstack/single.json.erb +10 -0
  134. data/templates/cluster/single-blank.json.erb +8 -0
  135. data/templates/cluster/single.json.erb +8 -0
  136. data/templates/init/knife.rb.erb +13 -0
  137. data/templates/kitchen/Cheffile.erb +11 -0
  138. data/templates/kitchen/kitchen.aws.yml.erb +35 -0
  139. data/templates/kitchen/kitchen.docker.yml.erb +24 -0
  140. data/templates/kitchen/kitchen.generate.yml.erb +2 -0
  141. data/templates/kitchen/kitchen.openstack.yml.erb +31 -0
  142. metadata +506 -0
@@ -0,0 +1,45 @@
1
+ require 'fhcap/tasks/chef/chef_task_base'
2
+
3
+ module Fhcap
4
+ module Tasks
5
+ module Chef
6
+ module Server
7
+ class Provision < ChefTaskBase
8
+
9
+ attr_reader :environments, :node_names, :roles
10
+
11
+ def initialize(options)
12
+ super
13
+ @environments = options[:environments]
14
+ @node_names = options[:nodes]
15
+ @roles = options[:roles]
16
+ @chef_server = options[:chef_server]
17
+ end
18
+
19
+ def run
20
+ thor.say "Chef::Server::Provision: environments = #{environments}", :yellow
21
+
22
+ chef_server_environments = {}
23
+ environments.each do |environment|
24
+ chef_server = select_chef_server(environment, @chef_server)
25
+ chef_server_environments[chef_server] = chef_server_environments[chef_server] || []
26
+ chef_server_environments[chef_server] << environment
27
+ end
28
+
29
+ chef_server_environments.each do |server, environments|
30
+ environments.each do |environment|
31
+ query = ["chef_environment:#{environment}"]
32
+ query << "AND name:#{node_names}" if node_names
33
+ query << "AND roles:#{roles}" if roles
34
+ query = query.join(' ')
35
+ cmd = "sudo chef-client"
36
+ run_knife_ssh_cmd(query, cmd, server, options)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,34 @@
1
+ require 'fhcap/tasks/task_base'
2
+
3
+ module Fhcap
4
+ module Tasks
5
+ class Clean < TaskBase
6
+
7
+ def initialize(options)
8
+ super
9
+ end
10
+
11
+ def run
12
+ if config.exists?
13
+ repos = config[:repos]
14
+ delete_files = repos.collect do |name, cfg|
15
+ repo_dir(name)
16
+ end.compact
17
+ delete_files << config.config_file
18
+
19
+ delete_files.each do |file|
20
+ thor.say_status 'removing', file, :yellow
21
+ end
22
+ if thor.yes? "Continue (y/n)"
23
+ delete_files.each do |file|
24
+ thor.remove_file file
25
+ end
26
+ end
27
+ else
28
+ thor.say_status 'error', "No config file found #{config.config_file}", :red
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,57 @@
1
+ require 'fhcap/tasks/task_base'
2
+
3
+ module Fhcap
4
+ module Tasks
5
+ module Cluster
6
+ class ClusterTaskBase < TaskBase
7
+
8
+ attr_reader :name, :cluster_config, :cluster_file
9
+
10
+ def initialize(options, exit_on_error=true)
11
+ super(options)
12
+ @name = options[:name]
13
+ @cluster_config = options[:'cluster-config']
14
+
15
+ unless @cluster_config
16
+ @cluster_file = find_cluster(@name)
17
+ if @cluster_file
18
+ @cluster_config = JSON.parse(IO.read(@cluster_file), {:symbolize_names => true})
19
+ else
20
+ exit_with_error("Unknown cluster #{@name}") if exit_on_error
21
+ @cluster_config = {}
22
+ end
23
+ end
24
+ end
25
+
26
+ def template_as_object(template_file, config)
27
+ template = File.read(template_file)
28
+ eruby = Erubis::Eruby.new(template)
29
+ result = eruby.result({config: config})
30
+ JSON.parse(result, {:symbolize_names => true})
31
+ end
32
+
33
+ def checkout_repos
34
+ repos = ['fhcap', cluster_config[:repo]].compact.uniq
35
+ repos.each do |name|
36
+ name = name.to_s
37
+ if @options[:'git-ref']
38
+ fallback_git_ref = name == 'fhcap' ? nil : 'master'
39
+ else
40
+ fallback_git_ref = nil
41
+ end
42
+ Repo::Checkout.new(@options.dup.merge({:repo => name, :'fallback-git-ref' => fallback_git_ref})).run
43
+ end if repos
44
+ end
45
+
46
+ private
47
+
48
+ def cluster_environments
49
+ @cluster_config[:environments].collect do |env_name, cfg|
50
+ [@name.to_s, env_name.to_s].uniq.join('-')
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,243 @@
1
+ require 'fhcap/tasks/cluster/cluster_task_base'
2
+ require 'fhcap/tasks/cluster/create_environment'
3
+ require 'fhcap/tasks/chef/provisioning/create'
4
+ require 'fhcap/tasks/chef/environments/promote_cookbooks'
5
+ require 'fhcap/tasks/chef/environments/create'
6
+ require 'fhcap/tasks/chef/server/bootstrap'
7
+ require 'fhcap/tasks/repo/checkout'
8
+ require "fhcap/tasks/misc/create_dns_record"
9
+
10
+ module Fhcap
11
+ module Tasks
12
+ module Cluster
13
+ class Create < ClusterTaskBase
14
+
15
+ attr_reader :name, :cluster_config
16
+
17
+ def initialize(options)
18
+ super(options, false)
19
+ @template = options[:template]
20
+ @repo = options[:'repo']
21
+ @provider_config = options[:'provider-config'] || {}
22
+ @gitref = options[:'git-ref']
23
+ @domain = options[:domain]
24
+ @ssl_cert = options[:'ssl-cert']
25
+ @dns_provider_id = options[:'dns-provider-id']
26
+ @chef_server = options[:'chef-server']
27
+ @provider_id = options[:'provider-id']
28
+ @skip_repo_checkout = options[:'skip-repo-checkout']
29
+ @skip_cookbook_promote = options[:'skip-cookbook-promote']
30
+ @skip_server_bootstrap = options[:'skip-server-bootstrap']
31
+ @skip_dns_record = options[:'skip-dns-record']
32
+ @skip_create = options[:'skip-create']
33
+
34
+ @cluster_config = {
35
+ id: @name,
36
+ template: @template,
37
+ driver: "",
38
+ domain: @domain,
39
+ repo: @repo,
40
+ chef_server: @chef_server,
41
+ provider_id: @provider_id
42
+ }.merge(@cluster_config)
43
+
44
+ @cluster_config[:provider_config] = @cluster_config[:provider_config] || {}
45
+ @cluster_config[:provider_config].merge!(@provider_config)
46
+ end
47
+
48
+ def run
49
+ thor.say "Cluster::Create: name = #{@name}", :yellow
50
+
51
+ #Checkout repos
52
+ checkout_repos unless @skip_repo_checkout
53
+
54
+ #Generate cluster config
55
+ generate_cluster_config
56
+
57
+ #Create environment files
58
+ create_cluster_environments
59
+
60
+ #Write config to disk
61
+ cluster_file = find_cluster(name) || File.join(repo_dir(cluster_config[:repo]), repo_clusters_dir(cluster_config[:repo]), "#{name}.json")
62
+ thor.create_file(cluster_file, JSON.pretty_generate(cluster_config))
63
+
64
+ #Chef Stuff
65
+ #Promote cookbooks
66
+ Chef::Environments::PromoteCookbooks.new(@options.dup.merge({:environments => cluster_environments})).run unless @skip_cookbook_promote
67
+
68
+ #Bootstrap Server
69
+ Chef::Server::Bootstrap.new(@options.dup.merge({:environments => cluster_environments, :chef_server => @cluster_config[:chef_server]})).run unless @skip_server_bootstrap
70
+
71
+ #Provisioning create
72
+ Chef::Provisioning::Create.new(options).run unless @skip_create
73
+
74
+ create_dns_record unless @skip_dns_record
75
+ end
76
+
77
+ private
78
+
79
+ def required_config
80
+ {
81
+ repo: {
82
+ msg: "In which repo should this clusters config and environment files be stored?",
83
+ options: {:limited_to => repo_names}
84
+ },
85
+ domain: {
86
+ },
87
+ chef_server: {
88
+ options: {:limited_to => chef_server_names}
89
+ },
90
+ provider_id: {
91
+ options: {:limited_to => provider_names_for('compute')}
92
+ },
93
+ template: {
94
+ options: {:limited_to => cluster_template_names}
95
+ }
96
+ }
97
+ end
98
+
99
+ def aws_required_config
100
+ aws_regions = provider_config(@cluster_config[:provider_id])[:regions]
101
+ {
102
+ region: {
103
+ options: {:limited_to => aws_regions.keys.collect { |k| k.to_s }}
104
+ },
105
+ cidr: {}
106
+ }
107
+ end
108
+
109
+ def generate_cluster_config
110
+ ask_config(required_config, cluster_config)
111
+ cluster_config[:driver] = provider_type(cluster_config[:provider_id])
112
+ send(:"generate_cluster_config_#{cluster_config[:driver]}")
113
+ driver_template_file = File.join('templates', 'cluster', cluster_config[:driver], "common.json.erb")
114
+ driver_template_config = template_as_object(driver_template_file, cluster_config)
115
+ cluster_config.merge!(driver_template_config)
116
+ end
117
+
118
+ def generate_cluster_config_aws
119
+ ask_config(aws_required_config, cluster_config[:provider_config])
120
+ aws_regions = provider_config(cluster_config[:provider_id])[:regions]
121
+ cluster_config[:default_instance_options] = cluster_config[:default_instance_options] || {}
122
+ cluster_config[:default_instance_options][:image_id] = aws_regions[cluster_config[:provider_config][:region].to_sym][:base_image]
123
+ end
124
+
125
+ def generate_cluster_config_openstack
126
+ cluster_config[:default_instance_options] = cluster_config[:default_instance_options] || {}
127
+ cluster_config[:default_instance_options][:image_ref] = provider_config(@cluster_config[:provider_id])[:image_ref]
128
+ cluster_config[:default_instance_options][:flavor_ref] = provider_config(@cluster_config[:provider_id])[:flavor_ref]
129
+ cluster_config[:default_instance_options][:floating_ip_pool] = provider_config(@cluster_config[:provider_id])[:floating_ip_pool]
130
+ cluster_config[:default_instance_options][:ssh_username] = provider_config(@cluster_config[:provider_id])[:ssh_username]
131
+ end
132
+
133
+ def create_cluster_environments
134
+ env_template_file = File.join('templates', 'cluster', "#{cluster_config[:template]}.json.erb")
135
+ template_config = template_as_object(env_template_file, cluster_config)
136
+
137
+ template_config[:environments].each do |env|
138
+ env_template_file = File.join('templates', 'cluster', cluster_config[:driver], "#{env[:template]}.json.erb")
139
+ template_config = template_as_object(env_template_file, cluster_config)
140
+
141
+ env_domain = [env[:'domain-prefix'], cluster_config[:domain]].compact.join('.')
142
+ env_cfg = {
143
+ :name => name,
144
+ :'environment-name' => env[:name],
145
+ :domain => env_domain,
146
+ :template => env[:template],
147
+ :'cluster-config' => cluster_config,
148
+ :'skip-create-cluster-file' => true
149
+ }
150
+
151
+ CreateEnvironment.new(@options.dup.merge(env_cfg)).run
152
+ end
153
+ end
154
+
155
+ def create_dns_record
156
+ send(:"create_dns_record_#{cluster_config[:driver]}")
157
+ end
158
+
159
+ def create_dns_record_aws
160
+ cluster_config[:environments].each do |env_name, env_cfg|
161
+ env_cfg[:load_balancers].each do |lb_name, lb_cfg|
162
+ if lb_cfg[:scheme] == "internet-facing"
163
+ begin
164
+ require 'aws-sdk'
165
+
166
+ cfg = provider_config(cluster_config[:provider_id])
167
+ creds = cfg[:credentials]
168
+
169
+ elb = Aws::ElasticLoadBalancing::Client.new(
170
+ region: cluster_config[:provider_config][:region],
171
+ access_key_id: creds[:'aws-access-key'],
172
+ secret_access_key: creds[:'aws-secret-key']
173
+ )
174
+
175
+ lb = load_balancer_name_for(name, env_name, lb_name)
176
+ resp = elb.describe_load_balancers({
177
+ load_balancer_names: [lb],
178
+ page_size: 1,
179
+ })
180
+
181
+ elb = resp.load_balancer_descriptions.first
182
+ thor.say "Found ELB name: #{elb.load_balancer_name}, dns_name: #{elb.dns_name}, zone_id: #{elb.canonical_hosted_zone_name_id}"
183
+
184
+ dns_record_cfg = {
185
+ domain: "*.#{env_cfg[:domain]}",
186
+ :"alias-target" => {
187
+ dns_name: elb.dns_name,
188
+ hosted_zone_id: elb.canonical_hosted_zone_name_id
189
+ }
190
+ }
191
+ Misc::CreateDNSRecord.new(@options.dup.merge(dns_record_cfg)).run
192
+ rescue Aws::ElasticLoadBalancing::Errors::LoadBalancerNotFound => e
193
+ thor.say_status 'error', "LoadBalancer #{lb} not found, unable to create DNS Entry", :red
194
+ rescue Aws::ElasticLoadBalancing::Errors::ServiceError => e
195
+ thor.say_status 'error', e.message, :red
196
+ end
197
+ end
198
+ end if env_cfg[:load_balancers]
199
+ end
200
+ end
201
+
202
+ def create_dns_record_openstack
203
+ cluster_config[:environments].each do |env_name, env_cfg|
204
+ env_name = "#{name}-#{env_name}"
205
+ knife_config_file = knife_config_file_for(cluster_config[:chef_server])
206
+ nodes = JSON.parse(`knife search "chef_environment:#{env_name} AND recipes:nginx_feedhenry\\:\\:loadbalancer" -c #{knife_config_file} -F json -a name -a cloud.public_ipv4`)
207
+
208
+ lb_node = nodes['rows'].collect do |row|
209
+ name, attrs = row.first
210
+ attrs
211
+ end.first
212
+
213
+ if lb_node
214
+ if lb_node['cloud.public_ipv4']
215
+ dns_record_cfg = {
216
+ domain: "*.#{env_cfg[:domain]}",
217
+ ipaddress: lb_node['cloud.public_ipv4']
218
+ }
219
+ Misc::CreateDNSRecord.new(@options.dup.merge(dns_record_cfg)).run
220
+ else
221
+ thor.say "Found lb node '#{lb_node['name']}', but was unable to retrieve it's IP!!}"
222
+ end
223
+ else
224
+ thor.say "Unable to locate lb node in cluster!!"
225
+ end
226
+ end
227
+ end
228
+
229
+ def load_balancer_name_for(org_name, environment, lb_name)
230
+ fh_name_for('fh-lb', org_name, environment, lb_name)
231
+ end
232
+
233
+ def fh_name_for(*args)
234
+ args.collect do |str|
235
+ str.to_s.split('-')
236
+ end.flatten.uniq.join('-')
237
+ end
238
+
239
+
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,171 @@
1
+ require 'fhcap/tasks/cluster/cluster_task_base'
2
+ require 'fhcap/tasks/chef/environments/create'
3
+ require "fhcap/tasks/misc/create_ssl_cert"
4
+
5
+ module Fhcap
6
+ module Tasks
7
+ module Cluster
8
+ class CreateEnvironment < ClusterTaskBase
9
+
10
+ attr_reader :template, :environment_name, :domain, :environment_config
11
+
12
+ def initialize(options)
13
+ super(options)
14
+ @skip_create_cluster_file = options[:'skip-create-cluster-file']
15
+ @template = options[:template]
16
+ @environment_name = options[:'environment-name']
17
+ @domain = options[:domain]
18
+
19
+ @cluster_config[:environments] = @cluster_config[:environments] || {}
20
+ @environment_config = @cluster_config[:environments][@environment_name.to_sym] || {}
21
+
22
+ template_filepath = File.join(Fhcap.source_root, 'templates', 'cluster', @cluster_config[:driver], "#{template}.json.erb")
23
+ eruby = Erubis::Eruby.new(File.read(template_filepath))
24
+ result = eruby.result(
25
+ {
26
+ config: {
27
+ domain: @domain,
28
+ environment_name: @environment_name
29
+ }
30
+ }
31
+ )
32
+ @environment_config.merge!(JSON.parse(result, {:symbolize_names => true}))
33
+
34
+ @availability_zones = provider_availability_zones(@cluster_config[:provider_id], @cluster_config[:provider_config][:region])
35
+ @cidr = @cluster_config[:provider_config][:cidr]
36
+ @subnets = {}
37
+ @allocated_ips = {}
38
+ @next_subnet = 0
39
+ end
40
+
41
+ def run
42
+ thor.say "Cluster::CreateEnvironment: cluster = #{name}, environment = #{environment_name}, domain = #{domain}", :yellow
43
+ env_cfg = {
44
+ name: "#{name}-#{environment_name}",
45
+ domain: domain,
46
+ type: get_env_type(environment_name),
47
+ repo: cluster_config[:repo]
48
+ }
49
+ generate_environment_config
50
+ create_chef_environment(env_cfg)
51
+ create_ssl_cert(env_cfg)
52
+ thor.create_file(cluster_file, JSON.pretty_generate(cluster_config)) unless @skip_create_cluster_file
53
+ end
54
+
55
+
56
+ private
57
+
58
+ def generate_environment_config
59
+ environment_config[:subnets].each do |subnet_id, subnet_cfg|
60
+ subnet_cfg[:cidr] = get_subnet_cidr(subnet_id, subnet_cfg[:cidr])
61
+ subnet_cfg[:availability_zone] = get_availability_zone(subnet_id)
62
+ end if environment_config[:subnets]
63
+
64
+ environment_config[:instances].each do |instance_id, instance_cfg|
65
+ if instance_cfg[:aws]
66
+ instance_cfg[:aws][:private_ip_address] = get_subnet_private_ip(instance_cfg[:aws][:subnet]) if instance_cfg[:aws].has_key?(:private_ip_address)
67
+ end
68
+ end if environment_config[:instances]
69
+
70
+ if environment_config[:security_groups]
71
+ cluster_config[:security_groups] = cluster_config[:security_groups] || {}
72
+
73
+ environment_config[:security_groups].each do |name, cfg|
74
+ cluster_config[:security_groups][name.to_sym] = cfg
75
+ end
76
+ environment_config.delete(:security_groups)
77
+ end
78
+
79
+ cluster_config[:environments][environment_name.to_sym] = cluster_config[:environments][environment_name.to_sym] || {}
80
+ cluster_config[:environments][environment_name.to_sym].merge!(environment_config)
81
+ end
82
+
83
+ def create_chef_environment(env_cfg)
84
+ Chef::Environments::Create.new(@options.dup.merge(env_cfg)).run
85
+ end
86
+
87
+ def create_ssl_cert(env_cfg)
88
+ if provider_type(cluster_config[:provider_id]) == 'aws'
89
+ cert_format = 'pem'
90
+ cert_directory = File.join(repo_dir(cluster_config[:repo]), repo_clusters_dir(cluster_config[:repo]), 'ssl_certs')
91
+ else
92
+ cert_format = 'json'
93
+ cert_directory = File.join(repo_dir(cluster_config[:repo]), 'data_bags', 'nginx_ssl_certs')
94
+ end
95
+ cert_name = "wildcard.#{env_cfg[:domain]}"
96
+ cert_domain = "*.#{env_cfg[:domain]}"
97
+ domain_cert_cfg = {
98
+ name: cert_name,
99
+ domain: cert_domain,
100
+ format: cert_format,
101
+ directory: cert_directory
102
+ }
103
+ cluster_config[:environments].each do |env_name, env_cfg|
104
+ env_cfg[:load_balancers].each do |lb_name, lb_cfg|
105
+ lb_cfg[:listeners].each do |listener|
106
+ if listener[:server_certificate]
107
+ listener[:server_certificate] = cert_name
108
+ end
109
+ end
110
+ end if env_cfg[:load_balancers]
111
+ end
112
+ Misc::CreateSslCert.new(@options.dup.merge(domain_cert_cfg)).run
113
+ end
114
+
115
+ def get_subnet(subnet)
116
+ subnet = subnet.to_sym
117
+ unless @subnets[subnet]
118
+ @subnets[subnet] = {}
119
+ @allocated_ips[subnet] = []
120
+ end
121
+ @subnets[subnet]
122
+ end
123
+
124
+ def get_subnet_cidr(subnet, range)
125
+ subnet = subnet.to_sym
126
+ unless get_subnet(subnet)[:cidr]
127
+ @subnets[subnet][:cidr] = "#{@cidr.split('.')[0..2].join('.')}.#{range}"
128
+ end
129
+ @subnets[subnet][:cidr]
130
+ end
131
+
132
+ def get_availability_zone(subnet)
133
+ subnet = subnet.to_sym
134
+ unless get_subnet(subnet)[:avilability_zone]
135
+ @subnets[subnet][:avilability_zone] = @availability_zones[@next_subnet]
136
+ @next_subnet = @next_subnet < (@availability_zones.length - 1) ? @next_subnet + 1 : 0
137
+ end
138
+ @subnets[subnet][:avilability_zone]
139
+ end
140
+
141
+ def get_subnet_private_ip(subnet)
142
+ subnet = subnet.to_sym
143
+ require 'ipaddr'
144
+ range = IPAddr.new(@subnets[subnet][:cidr]).to_range
145
+ private_ip = nil
146
+ range.each do |ip|
147
+ next if @allocated_ips[subnet].include? ip
148
+ private_ip = ip
149
+ @allocated_ips[subnet] << private_ip
150
+ break
151
+ end
152
+ private_ip.to_s
153
+ end
154
+
155
+ def get_env_type(name)
156
+ case name
157
+ when /core/
158
+ 'core'
159
+ when /mbaas/
160
+ 'mbaas'
161
+ when /single/
162
+ 'single'
163
+ else
164
+ 'empty'
165
+ end
166
+ end
167
+
168
+ end
169
+ end
170
+ end
171
+ end