fhcap-cli 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rakeTasks +7 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +150 -0
- data/Rakefile +2 -0
- data/bin/fhcap +6 -0
- data/fhcap-cli.gemspec +44 -0
- data/lib/cookbooks/provision/libraries/provision.rb +140 -0
- data/lib/cookbooks/provision/metadata.rb +7 -0
- data/lib/cookbooks/provision/recipes/aws.rb +15 -0
- data/lib/cookbooks/provision/recipes/aws_cluster_create.rb +59 -0
- data/lib/cookbooks/provision/recipes/aws_cluster_create_elb.rb +61 -0
- data/lib/cookbooks/provision/recipes/aws_cluster_destroy.rb +52 -0
- data/lib/cookbooks/provision/recipes/cluster_create.rb +2 -0
- data/lib/cookbooks/provision/recipes/cluster_destroy.rb +2 -0
- data/lib/cookbooks/provision/recipes/cluster_destroy_instances.rb +11 -0
- data/lib/cookbooks/provision/recipes/cluster_provision.rb +4 -0
- data/lib/cookbooks/provision/recipes/cluster_provision_instances.rb +55 -0
- data/lib/cookbooks/provision/recipes/cluster_status.rb +24 -0
- data/lib/cookbooks/provision/recipes/common.rb +9 -0
- data/lib/cookbooks/provision/recipes/default.rb +5 -0
- data/lib/cookbooks/provision/recipes/openstack.rb +11 -0
- data/lib/cookbooks/provision/recipes/openstack_cluster_create.rb +11 -0
- data/lib/cookbooks/provision/recipes/openstack_cluster_destroy.rb +4 -0
- data/lib/cookbooks/provision/recipes/reset_rabbitmq.rb +49 -0
- data/lib/cookbooks/provision/recipes/restart_services.rb +24 -0
- data/lib/extensions/chef/provisioning.rb +21 -0
- data/lib/extensions/chef/provisioning/aws_driver/driver.rb +46 -0
- data/lib/extensions/chef/provisioning/chef_run_data.rb +18 -0
- data/lib/extensions/cheffish/merged_config.rb +9 -0
- data/lib/fhcap.rb +14 -0
- data/lib/fhcap/chef-dk/chef_runner.rb +94 -0
- data/lib/fhcap/cli.rb +75 -0
- data/lib/fhcap/cluster.rb +112 -0
- data/lib/fhcap/config.rb +104 -0
- data/lib/fhcap/cookbook.rb +75 -0
- data/lib/fhcap/dummy_node.rb +80 -0
- data/lib/fhcap/fhcap_helper.rb +9 -0
- data/lib/fhcap/kitchen.rb +235 -0
- data/lib/fhcap/knife.rb +74 -0
- data/lib/fhcap/knife_helper.rb +38 -0
- data/lib/fhcap/misc.rb +103 -0
- data/lib/fhcap/provider.rb +41 -0
- data/lib/fhcap/providers_helper.rb +60 -0
- data/lib/fhcap/repo.rb +52 -0
- data/lib/fhcap/repos_helper.rb +217 -0
- data/lib/fhcap/tasks/chef/chef_task_base.rb +82 -0
- data/lib/fhcap/tasks/chef/cookbook/list.rb +37 -0
- data/lib/fhcap/tasks/chef/cookbook/update_changelog.rb +63 -0
- data/lib/fhcap/tasks/chef/cookbook/update_metadata.rb +57 -0
- data/lib/fhcap/tasks/chef/cookbook/update_readme.rb +30 -0
- data/lib/fhcap/tasks/chef/cookbook/update_version.rb +90 -0
- data/lib/fhcap/tasks/chef/environments/create.rb +115 -0
- data/lib/fhcap/tasks/chef/environments/destroy.rb +37 -0
- data/lib/fhcap/tasks/chef/environments/promote_cookbooks.rb +47 -0
- data/lib/fhcap/tasks/chef/provisioning/chef_provisioning_task.rb +27 -0
- data/lib/fhcap/tasks/chef/provisioning/chef_provisioning_task_base.rb +38 -0
- data/lib/fhcap/tasks/chef/provisioning/create.rb +22 -0
- data/lib/fhcap/tasks/chef/provisioning/destroy.rb +21 -0
- data/lib/fhcap/tasks/chef/provisioning/provision.rb +19 -0
- data/lib/fhcap/tasks/chef/server/bootstrap.rb +165 -0
- data/lib/fhcap/tasks/chef/server/create_user.rb +97 -0
- data/lib/fhcap/tasks/chef/server/info.rb +82 -0
- data/lib/fhcap/tasks/chef/server/provision.rb +45 -0
- data/lib/fhcap/tasks/clean.rb +34 -0
- data/lib/fhcap/tasks/cluster/cluster_task_base.rb +57 -0
- data/lib/fhcap/tasks/cluster/create.rb +243 -0
- data/lib/fhcap/tasks/cluster/create_environment.rb +171 -0
- data/lib/fhcap/tasks/cluster/destroy.rb +30 -0
- data/lib/fhcap/tasks/cluster/destroy_environment.rb +28 -0
- data/lib/fhcap/tasks/cluster/info.rb +67 -0
- data/lib/fhcap/tasks/cluster/list.rb +40 -0
- data/lib/fhcap/tasks/cluster/provision.rb +46 -0
- data/lib/fhcap/tasks/cluster/status.rb +17 -0
- data/lib/fhcap/tasks/cluster/test.rb +15 -0
- data/lib/fhcap/tasks/knife/add.rb +111 -0
- data/lib/fhcap/tasks/knife/list.rb +22 -0
- data/lib/fhcap/tasks/knife/remove.rb +39 -0
- data/lib/fhcap/tasks/misc/create_dns_record.rb +100 -0
- data/lib/fhcap/tasks/misc/create_ssl_cert.rb +82 -0
- data/lib/fhcap/tasks/provider/add.rb +136 -0
- data/lib/fhcap/tasks/provider/list.rb +31 -0
- data/lib/fhcap/tasks/provider/remove.rb +28 -0
- data/lib/fhcap/tasks/repo/add.rb +57 -0
- data/lib/fhcap/tasks/repo/checkout.rb +144 -0
- data/lib/fhcap/tasks/repo/list.rb +22 -0
- data/lib/fhcap/tasks/repo/remove.rb +34 -0
- data/lib/fhcap/tasks/setup.rb +59 -0
- data/lib/fhcap/tasks/task_base.rb +89 -0
- data/lib/fhcap/thor_base.rb +121 -0
- data/lib/fhcap/version.rb +3 -0
- data/spec/fhcap/cli_spec.rb +6 -0
- data/spec/fhcap/tasks/cluster/create_spec.rb +46 -0
- data/spec/fhcap/tasks/knife/add_spec.rb +35 -0
- data/spec/fhcap/tasks/knife/remove_spec.rb +25 -0
- data/spec/fhcap/tasks/provider/add_spec.rb +61 -0
- data/spec/fhcap/tasks/provider/remove_spec.rb +25 -0
- data/spec/fhcap/tasks/repo/add_spec.rb +32 -0
- data/spec/fhcap/tasks/repo/remove_spec.rb +25 -0
- data/spec/fhcap/tasks/task_base_spec.rb +51 -0
- data/spec/fhcap/thor_base_spec.rb +9 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/dummy_config.rb +7 -0
- data/spec/support/dummy_thor.rb +3 -0
- data/templates/chef/cookbook/changelog.md.erb +12 -0
- data/templates/chef/cookbook/metadata.erb +45 -0
- data/templates/chef/environment_core.json.erb +278 -0
- data/templates/chef/environment_empty.json.erb +10 -0
- data/templates/chef/environment_mbaas.json.erb +120 -0
- data/templates/chef/environment_single.json.erb +300 -0
- data/templates/cluster/aws/common.json.erb +43 -0
- data/templates/cluster/aws/core-3node.json.erb +106 -0
- data/templates/cluster/aws/core-small-9node.json.erb +333 -0
- data/templates/cluster/aws/mbaas-3node.json.erb +116 -0
- data/templates/cluster/aws/nginx-test.json.erb +93 -0
- data/templates/cluster/aws/single-blank.json.erb +41 -0
- data/templates/cluster/aws/single.json.erb +88 -0
- data/templates/cluster/core-3node.json.erb +8 -0
- data/templates/cluster/core-mbaas-6node.json.erb +13 -0
- data/templates/cluster/core-small-9node.json.erb +8 -0
- data/templates/cluster/mbaas-3node.json.erb +9 -0
- data/templates/cluster/nginx-test.json.erb +8 -0
- data/templates/cluster/openstack/common.json.erb +7 -0
- data/templates/cluster/openstack/core-3node.json.erb +14 -0
- data/templates/cluster/openstack/core-small-9node.json.erb +32 -0
- data/templates/cluster/openstack/mbaas-3node.json.erb +14 -0
- data/templates/cluster/openstack/nginx-test.json.erb +11 -0
- data/templates/cluster/openstack/single-blank.json.erb +10 -0
- data/templates/cluster/openstack/single.json.erb +10 -0
- data/templates/cluster/single-blank.json.erb +8 -0
- data/templates/cluster/single.json.erb +8 -0
- data/templates/init/knife.rb.erb +13 -0
- data/templates/kitchen/Cheffile.erb +11 -0
- data/templates/kitchen/kitchen.aws.yml.erb +35 -0
- data/templates/kitchen/kitchen.docker.yml.erb +24 -0
- data/templates/kitchen/kitchen.generate.yml.erb +2 -0
- data/templates/kitchen/kitchen.openstack.yml.erb +31 -0
- 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
|