fhcap-cli 0.4.5 → 0.4.6

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +1 -1
  5. data/fhcap-cli.gemspec +2 -1
  6. data/lib/cookbooks/provision/libraries/provision.rb +26 -11
  7. data/lib/cookbooks/provision/recipes/aws_cluster_create.rb +7 -0
  8. data/lib/cookbooks/provision/recipes/aws_cluster_create_elb.rb +6 -1
  9. data/lib/cookbooks/provision/recipes/aws_cluster_destroy.rb +17 -16
  10. data/lib/cookbooks/provision/recipes/cluster_bootstrap.rb +4 -0
  11. data/lib/cookbooks/provision/recipes/cluster_bootstrap_instances.rb +37 -0
  12. data/lib/cookbooks/provision/recipes/cluster_create_instances.rb +11 -9
  13. data/lib/cookbooks/provision/recipes/cluster_provision_instances.rb +12 -11
  14. data/lib/cookbooks/provision/recipes/galera_reset_cluster.rb +40 -36
  15. data/lib/cookbooks/provision/recipes/mongo_reset_cluster.rb +10 -9
  16. data/lib/cookbooks/provision/recipes/rabbitmq_reset_cluster.rb +3 -1
  17. data/lib/extensions/chef/solr/query/regexpable_query.rb +17 -0
  18. data/lib/fhcap/chef-dk/chef_runner.rb +4 -1
  19. data/lib/fhcap/cluster.rb +66 -14
  20. data/lib/fhcap/cookbook.rb +1 -0
  21. data/lib/fhcap/kitchen.rb +27 -4
  22. data/lib/fhcap/provider.rb +1 -1
  23. data/lib/fhcap/repos_helper.rb +1 -1
  24. data/lib/fhcap/tasks/chef/chef_task_base.rb +3 -1
  25. data/lib/fhcap/tasks/chef/chef_zero_server.rb +1 -0
  26. data/lib/fhcap/tasks/chef/cookbook/update_version.rb +1 -2
  27. data/lib/fhcap/tasks/chef/provisioning/chef_provisioning_task_base.rb +19 -4
  28. data/lib/fhcap/tasks/chef/provisioning/create.rb +6 -3
  29. data/lib/fhcap/tasks/cluster/cluster_task_base.rb +1 -0
  30. data/lib/fhcap/tasks/cluster/create.rb +4 -201
  31. data/lib/fhcap/tasks/cluster/create_dns_records.rb +108 -0
  32. data/lib/fhcap/tasks/cluster/create_environment.rb +1 -0
  33. data/lib/fhcap/tasks/cluster/generate.rb +135 -0
  34. data/lib/fhcap/tasks/provider/add.rb +11 -0
  35. data/lib/fhcap/version.rb +1 -1
  36. data/spec/fhcap/tasks/cluster/create_dns_records_spec.rb +64 -0
  37. data/spec/fhcap/tasks/cluster/create_spec.rb +34 -13
  38. data/spec/fhcap/tasks/cluster/generate_spec.rb +44 -0
  39. data/templates/chef/environment_core.json.erb +8 -3
  40. data/templates/chef/environment_farm.json.erb +5 -0
  41. data/templates/chef/environment_mbaas.json.erb +11 -2
  42. data/templates/chef/environment_single.json.erb +17 -8
  43. data/templates/cluster/aws/common.json.erb +1 -1
  44. data/templates/cluster/aws/core-3node.json.erb +14 -2
  45. data/templates/cluster/aws/core-small-9node.json.erb +13 -1
  46. data/templates/cluster/aws/mbaas-3node.json.erb +14 -2
  47. data/templates/cluster/aws/single-blank.json.erb +6 -0
  48. data/templates/cluster/aws/single.json.erb +13 -1
  49. data/templates/cluster/openstack/core-3node.json.erb +1 -1
  50. data/templates/kitchen/kitchen.docker.yml.erb +0 -3
  51. data/templates/kitchen/kitchen.generate.yml.erb +0 -1
  52. data/templates/kitchen/kitchen.vagrant.yml.erb +37 -0
  53. metadata +29 -5
data/lib/fhcap/cluster.rb CHANGED
@@ -8,34 +8,70 @@ module Fhcap
8
8
  add_shared_option :verbose, :aliases => "-v", :desc => "Verbose output", :type => :boolean, :required => false, :default => false
9
9
  add_shared_option :name, :type => :string, :required => true, :desc => "Cluster name"
10
10
  add_shared_option :interactive, :aliases => "-i", :desc => "Interactive mode (Stops for input)", :type => :boolean, :required => false, :default => false
11
+ add_shared_option :'dry-run', :desc => "Dry Run only, nothing will be modified, but instead will display things that would be changed if this command was executed.", :type => :boolean, :required => false, :default => false
12
+ add_shared_option :'run-options', :type => :hash, :required => false, :desc => 'Run specific options, will be set as attributes on the node during the chef provisioning run!'
13
+
14
+ #Generate options
15
+ add_shared_option :template, :type => :string, :desc => "Cluster template i.e. [1, 3 , 5 node cluster setup, core or mbaas]", :enum => cluster_template_names
16
+ add_shared_option :domain, :type => :string, :desc => "host names to register with DNS / base host name"
17
+ add_shared_option :'ssl-cert', :type => :string, :desc => "ssl cert to use"
18
+ add_shared_option :'dns-provider-id', :type => :string, :desc => "DNS Provider Id"
19
+ add_shared_option :'repo', :type => :string, :desc => "Fhcap Repo where all config should be saved", :enum => repo_names
20
+ add_shared_option :'chef-server', :type => :string, :desc => "Chef Server to use", :enum => chef_server_names
21
+ add_shared_option :'provider-id', :type => :string, :desc => "Provider Id", :enum => provider_names_for('compute')
22
+ add_shared_option :'provider-config', :type => :hash, :desc => "Provider specific config"
23
+
24
+ desc "generate", "Generate/Update cluster configuration"
25
+ long_desc <<-LONGDESC
26
+ Generate/Update cluster configuration.
27
+
28
+ This task will generate/update all the configuration and chef files required to create a new cluster using the create task.
29
+ LONGDESC
30
+
31
+ shared_options :verbose, :name, :interactive, :'dry-run', :'run-options',
32
+ :template, :domain, :'ssl-cert', :'dns-provider-id', :'repo', :'chef-server', :'provider-id', :'provider-config'
33
+
34
+ def generate
35
+ require "fhcap/tasks/cluster/generate"
36
+ Tasks::Cluster::Generate.new(task_options(options.dup)).run
37
+ end
11
38
 
12
39
  desc "create", "Create a new cluster"
13
40
 
14
- shared_options :verbose, :name, :interactive
41
+ shared_options :verbose, :name, :interactive, :'dry-run',
42
+ :template, :domain, :'ssl-cert', :'dns-provider-id', :'repo', :'chef-server', :'provider-id', :'provider-config'
15
43
 
16
- method_option 'template', :type => :string, :desc => "Cluster template i.e. [1, 3 , 5 node cluster setup, core or mbaas]", :enum => cluster_template_names
17
- method_option 'git-ref', :type => :string, :desc => "fhcap git ref, can be a branch, tag or commit hash"
18
- method_option 'git-remote', :type => :string, :desc => "git remote to use, default is the remote for the configured repos url"
19
- method_option 'domain', :type => :string, :desc => "host names to register with DNS / base host name"
20
- method_option 'ssl-cert', :type => :string, :desc => "ssl cert to use"
21
- method_option 'dns-provider-id', :type => :string, :desc => "DNS Provider Id"
22
- method_option 'repo', :type => :string, :desc => "Fhcap Repo where all config should be saved", :enum => repo_names
23
- method_option 'chef-server', :type => :string, :desc => "Chef Server to use", :enum => chef_server_names
24
- method_option 'provider-id', :type => :string, :desc => "Provider Id", :enum => provider_names_for('compute')
25
- method_option 'provider-config', :type => :hash, :desc => "Provider specific config"
44
+ method_option :generate, :type => :boolean, :default => true, :desc => "Run generate task before create"
45
+ method_option :'git-ref', :type => :string, :desc => "fhcap git ref, can be a branch, tag or commit hash"
46
+ method_option :'git-remote', :type => :string, :desc => "git remote to use, default is the remote for the configured repos url"
26
47
  create_steps = %w{repo-checkout cookbook-promote server-bootstrap create provision dns-record}
27
48
  create_steps.each do |step|
28
49
  method_option :"skip-#{step}", :type => :boolean, :default => false, :desc => "Skip #{step}"
29
50
  end
30
51
 
31
52
  def create
53
+ require "fhcap/tasks/cluster/generate"
32
54
  require "fhcap/tasks/cluster/create"
55
+ Tasks::Cluster::Generate.new(task_options(options.dup)).run if options[:generate]
33
56
  Tasks::Cluster::Create.new(task_options(options.dup)).run
34
57
  end
35
58
 
36
- desc "provision", "Provision a cluster"
59
+ desc "create_dns_records", "Create/Update a clusters DNS records"
60
+ long_desc <<-LONGDESC
61
+ Create/Update a clusters DNS records.
62
+ LONGDESC
37
63
 
38
64
  shared_options :verbose, :name
65
+
66
+ def create_dns_records
67
+ require "fhcap/tasks/cluster/create_dns_records"
68
+ Tasks::Cluster::CreateDNSRecords.new(task_options(options.dup)).run
69
+ end
70
+
71
+ desc "provision", "Provision a cluster"
72
+
73
+ shared_options :verbose, :name, :'dry-run', :'run-options'
74
+
39
75
  method_option 'git-ref', :type => :string, :desc => "fhcap git ref, can be a branch, tag or commit hash"
40
76
  method_option 'git-remote', :type => :string, :desc => "git remote to use, default is the remote for the configured repos url"
41
77
  method_option 'nodes', :type => :string, :desc => "Query to pass to name"
@@ -74,7 +110,7 @@ module Fhcap
74
110
 
75
111
  desc "destroy", "Destroy a cluster"
76
112
 
77
- shared_options :verbose
113
+ shared_options :verbose, :'dry-run'
78
114
 
79
115
  method_option 'name', :type => :string, :required => true, :desc => "Cluster name"
80
116
 
@@ -109,8 +145,24 @@ module Fhcap
109
145
  end
110
146
 
111
147
  desc "chef_provision", "[ADVANCED USAGE] Run a chef provisioning task against a particular cluster"
148
+ long_desc <<-LONGDESC
149
+ Allows the execution of any chef provisioning recipe on the given cluster.
150
+ Available recipes can be viewed here https://github.com/fheng/fhcap-cli/tree/master/lib/cookbooks/provision/recipes
112
151
 
113
- shared_options :verbose
152
+ With --run-options option, allows advanced configuration to be applied during the chef provisioning run.
153
+ These key:value pairs are added as attributes to the provisioning node and can alter the behaviour of the run.
154
+
155
+ env-regexp: Filter environments e.g. --run-options env-regexp:farm - Only environments with farm in the name will be acted upon (qed1-farm)
156
+
157
+ node-regexp: Filter nodes e.g. --run-options node-regexp:app - Only nodes with app in the name will be acted upon (qed1-core-app1, qed1-core-app2)
158
+
159
+ Examples:
160
+
161
+ fhcap cluster chef_provision --name qed1 --run-list provision::cluster_provision_instances
162
+
163
+ fhcap cluster chef_provision --name qed1 --run-list provision::cluster_provision_instances --run-options env-regexp:farm node-regexp:linux
164
+ LONGDESC
165
+ shared_options :verbose, :'dry-run', :'run-options'
114
166
 
115
167
  method_option 'name', :type => :string, :required => true, :desc => 'Cluster name'
116
168
  method_option 'run-list', :type => :array, :required => true, :desc => 'Chef Provisioning Run List i.e provision::cluster_provision'
@@ -35,6 +35,7 @@ module Fhcap
35
35
 
36
36
  method_option 'level', :type => :string, :aliases => "-l", :desc => "Cookbook Bump level", :default => 'minor', :enum => %w{major minor patch}
37
37
  method_option 'version', :type => :string, :aliases => "-v", :desc => "Version to set manually"
38
+ method_option 'base-branch', :type => :string, :default => 'origin/master', :desc => "Base branch to check modified cookbooks against"
38
39
 
39
40
  def update
40
41
  require "fhcap/tasks/chef/cookbook/update_version"
data/lib/fhcap/kitchen.rb CHANGED
@@ -13,14 +13,16 @@ module Fhcap
13
13
 
14
14
  add_shared_option :cookbooks, :aliases => "-c", :desc => "Apply to these Cookbooks", :type => :array
15
15
  add_shared_option :roles, :aliases => "-r", :desc => "Roles to generate kitchen for", :type => :array
16
- add_shared_option :driver, :aliases => "-d", :desc => "Kitchen Driver", :type => :string, :default => 'openstack', :enum => %w{aws openstack docker}
16
+ add_shared_option :driver, :aliases => "-d", :desc => "Kitchen Driver", :type => :string, :default => 'openstack', :enum => %w{aws openstack docker vagrant}
17
17
  add_shared_option :modified, :aliases => "-m", :desc => "Apply only to modified Cookbooks", :type => :boolean, :required => false, :default => false
18
18
  add_shared_option :provisioner, :aliases => "-p", :desc => "Kitchen Provisioner", :type => :string, :default => 'chef_solo', :enum => %w{chef_solo chef_zero}
19
+ add_shared_option :'base-branch', :type => :string, :default => 'origin/master', :desc => "Base branch to check modified cookbooks against"
20
+ add_shared_option :'kitchen-options', :type => :hash, :default => {}, :desc => "Options to pass to kitchen command i.e. concurrency:10 = --concurrency=10 "
19
21
 
20
22
 
21
23
  desc "generate", "Generate"
22
24
 
23
- shared_options :cookbooks, :roles, :modified, :driver, :provisioner
25
+ shared_options :cookbooks, :roles, :modified, :driver, :provisioner, :'base-branch', :'kitchen-options'
24
26
  method_option 'only', :type => :array, :default => nil, :desc => "Only include tests for cookbooks matching these names."
25
27
  method_option 'exclude', :type => :array, :default => nil, :desc => "Exclude tests for cookbooks matching these names, takes precedence over 'only' option."
26
28
  method_option 'clean', :type => :boolean, :default => true, :desc => "Clean the current generated kitchen prior to creating the new one"
@@ -128,16 +130,24 @@ module Fhcap
128
130
  %w{create converge setup verify test list login destroy diagnose}.each do |cmd|
129
131
 
130
132
  desc "#{cmd} [INSTANCE|REGEXP|all]", "Run '#{cmd}' command against current kitchen instances."
131
- shared_options :cookbooks, :roles, :modified, :driver, :provisioner if %w{create converge setup verify test}.include? cmd
133
+ shared_options :cookbooks, :roles, :modified, :driver, :provisioner, :'base-branch', :'kitchen-options' if %w{create converge setup verify test list}.include? cmd
134
+ method_option :'log-level', :aliases => "-l", :desc => "Set the log level", :type => :string, :default => 'info', :enum => %w{debug info warn error fatal}
132
135
 
133
136
  define_method(cmd) do |instance='all'|
137
+ kitchen_options = options[:'kitchen-options'] || {}
134
138
  Fhcap::CLI::Kitchen.new.invoke(:generate, [], options) if options[:cookbooks] || options[:modified] || options[:roles]
135
139
  if is_kitchen_generated?
136
140
  Dir.chdir test_suite_dir do
137
141
  cmd_options = options.dup
138
142
  cmd_options['instance'] = instance
139
143
  cmd_options['kitchen-yaml'] = test_suite_kitchen_yml
140
- cmd_options['kitchen-options'] = '--concurrency=10'
144
+ cmd_options['kitchen-options'] = kitchen_options_for(
145
+ default_kitchen_options.merge(
146
+ {:'log-level' => options[:'log-level']}
147
+ ).merge(
148
+ JSON.parse(kitchen_options.to_json, {:symbolize_names => true})
149
+ )
150
+ )
141
151
  run_kitchen_cmd(cmd, cmd_options)
142
152
  end
143
153
  else
@@ -149,6 +159,19 @@ module Fhcap
149
159
 
150
160
  protected
151
161
 
162
+ def kitchen_options_for(opts)
163
+ opts.collect do |key,val|
164
+ "--#{key}=#{val}"
165
+ end.join(' ')
166
+ end
167
+
168
+ def default_kitchen_options
169
+ {
170
+ :concurrency => 10,
171
+ :'log-level' => 'info'
172
+ }
173
+ end
174
+
152
175
  def test_suite_dir
153
176
  File.join(config.default_dir, 'tmp', 'kitchen')
154
177
  end
@@ -8,7 +8,7 @@ module Fhcap
8
8
  add_shared_option :verbose, :aliases => "-v", :desc => "Verbose output", :type => :boolean, :required => false, :default => false
9
9
  add_shared_option :interactive, :aliases => "-i", :desc => "Interactive mode (Stops for input)", :type => :boolean, :required => false, :default => false
10
10
  add_shared_option :name, :type => :string, :required => true, :desc => "Provider Name"
11
- add_shared_option :type, :type => :string, :required => true, :desc => "Provider Type", :enum => %w{aws openstack}
11
+ add_shared_option :type, :type => :string, :required => true, :desc => "Provider Type", :enum => %w{aws openstack dnsmadeeasy}
12
12
  add_shared_option :credentials, :type => :hash, :desc => "Provider specific credentials"
13
13
 
14
14
  desc "add", "Add a provider"
@@ -134,7 +134,7 @@ module Fhcap
134
134
  modified = []
135
135
  cookbook_loader(paths).cookbook_names.each do |name|
136
136
  cookbook_dir = cookbook_loader.cookbooks_by_name[name].root_dir
137
- modified << name if modified?(cookbook_dir)
137
+ modified << name if modified?(cookbook_dir, options[:'base-branch'] || 'origin/master')
138
138
  end
139
139
  modified
140
140
  end
@@ -21,7 +21,9 @@ module Fhcap
21
21
  user = options[:knife_user] || 'hadmin'
22
22
  attr = options[:knife_attr] || 'cloud.public_ipv4'
23
23
  knife_options = options[:knife_options]
24
- run_knife_cmd("ssh \"#{query}\" \"#{cmd}\" -x #{user} -a #{attr} #{knife_options}", server, false)
24
+ ssh_cmd = ["ssh \"#{query}\" \"#{cmd}\" -x #{user} -a #{attr} #{knife_options}"]
25
+ ssh_cmd << "--concurrency 1" if options[:series]
26
+ run_knife_cmd(ssh_cmd.join(' '), server, false)
25
27
  end
26
28
 
27
29
  def run_knife_cmd(cmd, server, capture=true)
@@ -1,4 +1,5 @@
1
1
  require 'chef_zero/server'
2
+ require 'extensions/chef/solr/query/regexpable_query'
2
3
 
3
4
  module Fhcap
4
5
  module Tasks
@@ -16,7 +16,6 @@ module Fhcap
16
16
  end
17
17
 
18
18
  def run
19
- thor.say "Chef::Cookbook::UpdateVersion cookbook = #{name}", :yellow
20
19
  cookbook = cookbook_loader.cookbooks_by_name[name]
21
20
  metadata_file = cookbook.metadata_filenames.first
22
21
 
@@ -51,7 +50,7 @@ module Fhcap
51
50
  new_contents = File.read(metadata_file).gsub(/(version\s+['"])[0-9\.]+(['"])/, "\\1#{new_version}\\2")
52
51
  File.open(metadata_file, 'w') { |f| f.write(new_contents) }
53
52
 
54
- thor.say "Successfully bumped #{cookbook.name} to v#{new_version}!"
53
+ thor.say "Updated #{cookbook.name} cookbook version to v#{new_version}!"
55
54
  end
56
55
 
57
56
  def bump_type
@@ -1,6 +1,7 @@
1
1
  require 'fhcap/tasks/chef/chef_server_task'
2
2
  require 'fhcap/tasks/chef/cookbook/archive'
3
3
  require 'fhcap/chef-dk/chef_runner'
4
+ require 'deep_merge'
4
5
 
5
6
  module Fhcap
6
7
  module Tasks
@@ -8,7 +9,7 @@ module Fhcap
8
9
  module Provisioning
9
10
  class ChefProvisioningTaskBase < ChefServerTask
10
11
 
11
- attr_reader :name, :cluster_config, :cluster_filepath
12
+ attr_reader :name, :cluster_config, :cluster_filepath, :run_options
12
13
 
13
14
  def initialize(options)
14
15
  super
@@ -20,12 +21,13 @@ module Fhcap
20
21
  local_repo_path: repo_dir(@chef_repo),
21
22
  local_repo_clusters_dir: repo_clusters_dir(@chef_repo),
22
23
  chef_server_config: chef_server_config_hash_for(@chef_server),
23
- provider_credentials: provider_credentials(@cluster_config[:provider_id]),
24
+ provider_credentials: provider_credentials(@cluster_config[:provider_id]),
24
25
  max_simultaneous: options[:series] ? 1 : nil
25
26
  })
26
27
  else
27
28
  exit_with_error("Unknown cluster #{@name}")
28
29
  end
30
+ @run_options = options[:'run-options']
29
31
  end
30
32
 
31
33
  def seed_cookbooks
@@ -35,10 +37,23 @@ module Fhcap
35
37
  do_chef_run("provision::seed_cookbooks")
36
38
  end
37
39
 
38
- def do_chef_run(run_list)
40
+ def default_run_options
41
+ {
42
+ max_simultaneous: options[:series] ? 1 : nil,
43
+ "env-regexp" => '.',
44
+ "node-regexp" => '.'
45
+ }
46
+ end
47
+
48
+ def get_run_options(opts)
49
+ default_run_options.deep_merge!(opts)
50
+ end
51
+
52
+ def do_chef_run(run_list, dry_run=options[:'dry-run'])
39
53
  cookbook_path = File.join(Fhcap.source_root, 'lib', 'cookbooks')
40
54
  private_key_paths = [File.join(cluster_config[:local_repo_path], cluster_config[:local_repo_clusters_dir], 'key_pairs')]
41
- Fhcap::ChefRunner.new(cookbook_path, run_list, cluster_config, private_key_paths).converge
55
+ node_attrs = cluster_config.deep_merge(get_run_options(run_options))
56
+ Fhcap::ChefRunner.new(cookbook_path, run_list, dry_run, node_attrs, private_key_paths).converge
42
57
  end
43
58
 
44
59
  end
@@ -14,8 +14,13 @@ module Fhcap
14
14
  knife_download(repo_dir(chef_repo), chef_server, ['/nodes']) if local_chef_server?
15
15
 
16
16
  unless options[:'skip-provision']
17
+ do_chef_run("provision::cluster_bootstrap")
17
18
  seed_cookbooks if local_chef_server?
18
19
  do_chef_run("provision::post_create_instances")
20
+
21
+ #Need to set run list correctly here after post create stuff since provision no longer does it
22
+ do_chef_run("provision::cluster_bootstrap")
23
+
19
24
  do_chef_run("provision::cluster_provision")
20
25
  end
21
26
  end
@@ -25,6 +30,4 @@ module Fhcap
25
30
  end
26
31
  end
27
32
  end
28
- end
29
-
30
-
33
+ end
@@ -1,4 +1,5 @@
1
1
  require 'fhcap/tasks/task_base'
2
+ require 'erubis'
2
3
 
3
4
  module Fhcap
4
5
  module Tasks
@@ -5,7 +5,7 @@ require 'fhcap/tasks/chef/environments/promote_cookbooks'
5
5
  require 'fhcap/tasks/chef/environments/create'
6
6
  require 'fhcap/tasks/chef/server/bootstrap'
7
7
  require 'fhcap/tasks/repo/checkout'
8
- require "fhcap/tasks/dns/create_record"
8
+ require "fhcap/tasks/cluster/create_dns_records"
9
9
 
10
10
  module Fhcap
11
11
  module Tasks
@@ -15,35 +15,14 @@ module Fhcap
15
15
  attr_reader :name, :cluster_config
16
16
 
17
17
  def initialize(options)
18
- super(options, false)
19
- @template = options[:template]
20
- @repo = options[:'repo']
21
- @provider_config = options[:'provider-config'] || {}
18
+ super(options)
22
19
  @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
20
  @skip_repo_checkout = options[:'skip-repo-checkout']
29
21
  @skip_cookbook_promote = options[:'skip-cookbook-promote']
30
22
  @skip_server_bootstrap = options[:'skip-server-bootstrap']
31
23
  @skip_dns_record = options[:'skip-dns-record']
32
24
  @skip_create = options[:'skip-create']
33
25
  @skip_provision = options[:'skip-provision']
34
-
35
- @cluster_config = {
36
- id: @name,
37
- template: @template,
38
- driver: "",
39
- domain: @domain,
40
- repo: @repo,
41
- chef_server: @chef_server,
42
- provider_id: @provider_id
43
- }.merge(@cluster_config)
44
-
45
- @cluster_config[:provider_config] = @cluster_config[:provider_config] || {}
46
- @cluster_config[:provider_config].merge!(@provider_config)
47
26
  end
48
27
 
49
28
  def run
@@ -52,16 +31,6 @@ module Fhcap
52
31
  #Checkout repos
53
32
  checkout_repos unless @skip_repo_checkout
54
33
 
55
- #Generate cluster config
56
- generate_cluster_config
57
-
58
- #Create environment files
59
- create_cluster_environments
60
-
61
- #Write config to disk
62
- cluster_file = find_cluster(name) || File.join(repo_dir(cluster_config[:repo]), repo_clusters_dir(cluster_config[:repo]), "#{name}.json")
63
- thor.create_file(cluster_file, JSON.pretty_generate(cluster_config))
64
-
65
34
  #Chef Stuff
66
35
  #Promote cookbooks
67
36
  Chef::Environments::PromoteCookbooks.new(@options.dup.merge({:environments => cluster_environments})).run unless @skip_cookbook_promote
@@ -72,176 +41,10 @@ module Fhcap
72
41
  #Provisioning create
73
42
  Chef::Provisioning::Create.new(@options.dup.merge(chef_task_options)).run unless @skip_create
74
43
 
75
- create_dns_record unless @skip_dns_record
76
- end
77
-
78
- private
79
-
80
- def required_config
81
- {
82
- repo: {
83
- msg: "In which repo should this clusters config and environment files be stored?",
84
- options: {:limited_to => repo_names}
85
- },
86
- domain: {
87
- msg: "Base domain/hostname (e.g. foo.skunkhenry.com) for this cluster?",
88
- },
89
- chef_server: {
90
- options: {:limited_to => chef_server_names}
91
- },
92
- provider_id: {
93
- options: {:limited_to => provider_names_for('compute')}
94
- },
95
- template: {
96
- options: {:limited_to => cluster_template_names}
97
- }
98
- }
99
- end
100
-
101
- def aws_required_config
102
- aws_regions = provider_config(@cluster_config[:provider_id])[:regions]
103
- {
104
- region: {
105
- options: {:limited_to => aws_regions.keys.collect { |k| k.to_s }}
106
- },
107
- cidr: {
108
- msg: "CIDR block for AWS VPC (e.g. 10.123.0.0/16)?",
109
- }
110
- }
44
+ #Create/Update DNS Records
45
+ Cluster::CreateDNSRecords.new(@options.dup.merge(chef_task_options)).run unless @skip_dns_record
111
46
  end
112
47
 
113
- def generate_cluster_config
114
- ask_config(required_config, cluster_config)
115
- cluster_config[:driver] = provider_type(cluster_config[:provider_id])
116
- send(:"generate_cluster_config_#{cluster_config[:driver]}")
117
- driver_template_file = File.join(Fhcap.source_root, 'templates', 'cluster', cluster_config[:driver], "common.json.erb")
118
- driver_template_config = template_as_object(driver_template_file, cluster_config)
119
- cluster_config.merge!(driver_template_config)
120
- end
121
-
122
- def generate_cluster_config_aws
123
- ask_config(aws_required_config, cluster_config[:provider_config])
124
- aws_regions = provider_config(cluster_config[:provider_id])[:regions]
125
- cluster_config[:default_instance_options] = cluster_config[:default_instance_options] || {}
126
- cluster_config[:default_instance_options][:image_id] = aws_regions[cluster_config[:provider_config][:region].to_sym][:base_image]
127
- end
128
-
129
- def generate_cluster_config_openstack
130
- cluster_config[:default_instance_options] = cluster_config[:default_instance_options] || {}
131
- cluster_config[:default_instance_options][:image_ref] = provider_config(@cluster_config[:provider_id])[:image_ref]
132
- cluster_config[:default_instance_options][:flavor_ref] = provider_config(@cluster_config[:provider_id])[:flavor_ref]
133
- cluster_config[:default_instance_options][:floating_ip_pool] = provider_config(@cluster_config[:provider_id])[:floating_ip_pool]
134
- cluster_config[:default_instance_options][:ssh_username] = provider_config(@cluster_config[:provider_id])[:ssh_username]
135
- end
136
-
137
- def create_cluster_environments
138
- env_template_file = File.join(Fhcap.source_root, 'templates', 'cluster', "#{cluster_config[:template]}.json.erb")
139
- template_config = template_as_object(env_template_file, cluster_config)
140
-
141
- template_config[:environments].each do |env|
142
- env_template_file = File.join(Fhcap.source_root, 'templates', 'cluster', cluster_config[:driver], "#{env[:template]}.json.erb")
143
- template_config = template_as_object(env_template_file, cluster_config)
144
-
145
- env_domain = [env[:'domain-prefix'], cluster_config[:domain]].compact.join('.')
146
- env_cfg = {
147
- :name => name,
148
- :'environment-name' => env[:name],
149
- :domain => env_domain,
150
- :template => env[:template],
151
- :'cluster-config' => cluster_config,
152
- :'skip-create-cluster-file' => true
153
- }
154
-
155
- CreateEnvironment.new(@options.dup.merge(env_cfg)).run
156
- end
157
- end
158
-
159
- def create_dns_record
160
- send(:"create_dns_record_#{cluster_config[:driver]}")
161
- end
162
-
163
- def create_dns_record_aws
164
- cluster_config[:environments].each do |env_name, env_cfg|
165
- env_cfg[:load_balancers].each do |lb_name, lb_cfg|
166
- if lb_cfg[:scheme] == "internet-facing"
167
- begin
168
- require 'aws-sdk'
169
-
170
- cfg = provider_config(cluster_config[:provider_id])
171
- creds = cfg[:credentials]
172
-
173
- elb = Aws::ElasticLoadBalancing::Client.new(
174
- region: cluster_config[:provider_config][:region],
175
- access_key_id: creds[:'aws-access-key'],
176
- secret_access_key: creds[:'aws-secret-key']
177
- )
178
-
179
- lb = load_balancer_name_for(name, env_name, lb_name)
180
- resp = elb.describe_load_balancers({
181
- load_balancer_names: [lb],
182
- page_size: 1,
183
- })
184
-
185
- elb = resp.load_balancer_descriptions.first
186
- thor.say "Found ELB name: #{elb.load_balancer_name}, dns_name: #{elb.dns_name}, zone_id: #{elb.canonical_hosted_zone_name_id}"
187
-
188
- dns_record_cfg = {
189
- domain: "*.#{env_cfg[:domain]}",
190
- :"alias-target" => {
191
- dns_name: elb.dns_name,
192
- hosted_zone_id: elb.canonical_hosted_zone_name_id
193
- }
194
- }
195
- Dns::CreateRecord.new(@options.dup.merge(dns_record_cfg)).run
196
- rescue Aws::ElasticLoadBalancing::Errors::LoadBalancerNotFound => e
197
- thor.say_status 'error', "LoadBalancer #{lb} not found, unable to create DNS Entry", :red
198
- rescue Aws::ElasticLoadBalancing::Errors::ServiceError => e
199
- thor.say_status 'error', e.message, :red
200
- end
201
- end
202
- end if env_cfg[:load_balancers]
203
- end
204
- end
205
-
206
- def create_dns_record_openstack
207
- cluster_config[:environments].each do |env_name, env_cfg|
208
- env_name = "#{name}-#{env_name}"
209
- knife_config_file = knife_config_file_for(cluster_config[:chef_server])
210
- #ToDo [RHMAP-2898] Use knife object
211
- 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`)
212
-
213
- lb_node = nodes['rows'].collect do |row|
214
- name, attrs = row.first
215
- attrs
216
- end.first
217
-
218
- if lb_node
219
- if lb_node['cloud.public_ipv4']
220
- dns_record_cfg = {
221
- domain: "*.#{env_cfg[:domain]}",
222
- ipaddress: lb_node['cloud.public_ipv4']
223
- }
224
- Dns::CreateRecord.new(@options.dup.merge(dns_record_cfg)).run
225
- else
226
- thor.say "Found lb node '#{lb_node['name']}', but was unable to retrieve it's IP!!}"
227
- end
228
- else
229
- thor.say "Unable to locate lb node in cluster!!"
230
- end
231
- end
232
- end
233
-
234
- def load_balancer_name_for(org_name, environment, lb_name)
235
- fh_name_for('fh-lb', org_name, environment, lb_name)[0..31] #elb names have a max length of 32
236
- end
237
-
238
- def fh_name_for(*args)
239
- args.collect do |str|
240
- str.to_s.split('-')
241
- end.flatten.uniq.join('-')
242
- end
243
-
244
-
245
48
  end
246
49
  end
247
50
  end