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,41 @@
1
+ require 'thor'
2
+ require 'fhcap/thor_base'
3
+
4
+ module Fhcap
5
+ module CLI
6
+ class Provider < Fhcap::ThorBase
7
+
8
+ add_shared_option :verbose, :aliases => "-v", :desc => "Verbose output", :type => :boolean, :required => false, :default => false
9
+ add_shared_option :interactive, :aliases => "-i", :desc => "Interactive mode (Stops for input)", :type => :boolean, :required => false, :default => false
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}
12
+ add_shared_option :credentials, :type => :hash, :desc => "Provider specific credentials"
13
+
14
+ desc "add", "Add a provider"
15
+
16
+ shared_options :verbose, :name, :type, :credentials, :interactive
17
+
18
+ def add
19
+ require "fhcap/tasks/provider/add"
20
+ Tasks::Provider::Add.new(task_options(options.dup)).run
21
+ end
22
+
23
+ desc "remove", "Remove a provider"
24
+
25
+ shared_options :verbose, :name, :type
26
+
27
+ def remove
28
+ require "fhcap/tasks/provider/remove"
29
+ Tasks::Provider::Remove.new(task_options(options.dup)).run
30
+ end
31
+
32
+ desc "list", "List configured providers"
33
+
34
+ def list
35
+ require "fhcap/tasks/provider/list"
36
+ Tasks::Provider::List.new(task_options(options.dup)).run
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,60 @@
1
+ module Fhcap
2
+ module ProvidersHelper
3
+
4
+ def providers_config
5
+ config.exists? && config[:providers] ? config[:providers] : {}
6
+ end
7
+
8
+ def provider_names
9
+ providers_config.collect do |provider, cfg|
10
+ cfg.collect do |id, pcfg|
11
+ "#{provider}:#{id}"
12
+ end
13
+ end.flatten
14
+ end
15
+
16
+ def provider_type(id)
17
+ provider_type, provider_id = id.split(':')
18
+ provider_type
19
+ end
20
+
21
+ def provider_config(id)
22
+ provider_type, provider_id = id.split(':')
23
+ providers_config[provider_type.to_sym][provider_id.to_sym]
24
+ end
25
+
26
+ def provider_credentials(id)
27
+ provider_config(id)[:credentials]
28
+ end
29
+
30
+ def provider_regions(id)
31
+ provider_config(id)[:regions] || {}
32
+ end
33
+
34
+ def provider_availability_zones(id, region)
35
+ return [] unless region && provider_regions(id)[region.to_sym]
36
+ provider_regions(id)[region.to_sym][:availability_zones] || []
37
+ end
38
+
39
+ def provider_names_for(service)
40
+ providers_config.collect do |provider, cfg|
41
+ cfg.reject do |id, pcfg|
42
+ !pcfg[:provides] || !pcfg[:provides].include?(service)
43
+ end.collect do |id, pcfg|
44
+ "#{provider}:#{id}"
45
+ end
46
+ end.flatten
47
+ end
48
+
49
+ def provider_for(domain)
50
+ providers_config.collect do |provider, cfg|
51
+ cfg.reject do |id, pcfg|
52
+ !pcfg[:provides] || !pcfg[:provides].include?('dns') || !pcfg[:domains].include?(domain)
53
+ end.collect do |id, pcfg|
54
+ "#{provider}:#{id}"
55
+ end
56
+ end.flatten.first
57
+ end
58
+
59
+ end
60
+ end
data/lib/fhcap/repo.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'thor'
2
+ require 'fhcap/thor_base'
3
+
4
+ module Fhcap
5
+ module CLI
6
+ class Repo < Fhcap::ThorBase
7
+
8
+ add_shared_option :verbose, :aliases => "-v", :desc => "Verbose output", :type => :boolean, :required => false, :default => false
9
+ add_shared_option :name, :type => :string, :required => true, :desc => "Repo name i.e. fhcap-dev"
10
+ add_shared_option :interactive, :aliases => "-i", :desc => "Interactive mode (Stops for input)", :type => :boolean, :required => false, :default => false
11
+
12
+ desc "add", "Add an fhcap repo"
13
+
14
+ shared_options :verbose, :name, :interactive
15
+ method_option 'url', :type => :string, :desc => "Repo url i.e. git@github.com:fheng/fhcap-dev.git"
16
+ method_option 'clusters-dir', :type => :string, :default => 'clusters', :desc => "Directlory in repo where cluster configs are stored i.e. organisations"
17
+
18
+ def add
19
+ require "fhcap/tasks/repo/add"
20
+ Tasks::Repo::Add.new(task_options(options.dup)).run
21
+ end
22
+
23
+ desc "remove", "Remove an fhcap repo"
24
+
25
+ shared_options :verbose, :name
26
+
27
+ def remove
28
+ require "fhcap/tasks/repo/remove"
29
+ Tasks::Repo::Remove.new(task_options(options.dup)).run
30
+ end
31
+
32
+ desc "checkout", "Checkout a configured repo"
33
+
34
+ shared_options :verbose, :name
35
+ method_option 'git-ref', :type => :string, :desc => "fhcap git ref, can be a branch, tag or commit hash"
36
+ method_option 'remote', :type => :string, :default => 'origin', :desc => "Git remote to pull from"
37
+
38
+ def checkout
39
+ require 'fhcap/tasks/repo/checkout'
40
+ Tasks::Repo::Checkout.new(task_options(options.dup)).run
41
+ end
42
+
43
+ desc "list", "List configured repos"
44
+
45
+ def list
46
+ require 'fhcap/tasks/repo/list'
47
+ Tasks::Repo::List.new(task_options(options.dup)).run
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,217 @@
1
+ module Fhcap
2
+ module ReposHelper
3
+
4
+ def repos_config
5
+ config.exists? && config[:repos] ? config[:repos] : {}
6
+ end
7
+
8
+ def repos_dir
9
+ config[:repos_dir] || config.default_dir
10
+ end
11
+
12
+ def repo_dir(name)
13
+ name = name.to_sym
14
+ if repos_config[name]
15
+ File.join(repos_dir, name.to_s)
16
+ end
17
+ end
18
+
19
+ def repo_cfg(name)
20
+ name = name.to_sym
21
+ repos_config[name]
22
+ end
23
+
24
+ def repo_paths
25
+ repos_config.collect do |name, cfg|
26
+ repo_dir(name)
27
+ end
28
+ end
29
+
30
+ def repo_names
31
+ repos_config.collect do |name, cfg|
32
+ name.to_s
33
+ end
34
+ end
35
+
36
+ def repo_clusters_dir(name)
37
+ name = name.to_sym
38
+ if repos_config[name]
39
+ repos_config[name][:clusters_dir] || 'clusters'
40
+ end
41
+ end
42
+
43
+ def run_inside_repo_dir(repo_dir, cmd)
44
+ status = -1
45
+ thor.inside(repo_dir) do
46
+ thor.run(cmd, :capture => !@verbose)
47
+ status = $?.exitstatus
48
+ end
49
+ status == 0
50
+ end
51
+
52
+ def find_data_bag(data_bag_name)
53
+ find_repo_item('data_bags', data_bag_name).first
54
+ end
55
+
56
+ def find_data_bag_item(data_bag_name, data_bag_item)
57
+ find_repo_item('data_bags', data_bag_name, ensure_json_extension(data_bag_item)).first
58
+ end
59
+
60
+ def find_environment(environment)
61
+ find_repo_item('environments', ensure_json_extension(environment)).first
62
+ end
63
+
64
+ def find_role(role)
65
+ find_repo_item('roles', ensure_json_extension(role)).first
66
+ end
67
+
68
+ def find_cluster(name)
69
+ find_cluster_with_repo(name).first
70
+ end
71
+
72
+ def find_cluster_with_repo(name)
73
+ find_repo_item('clusters', ensure_json_extension(name)) || []
74
+ end
75
+
76
+ def find_cluster_pwds(name)
77
+ find_cluster_pwds_with_repo(name).first
78
+ end
79
+
80
+ def find_cluster_pwds_with_repo(name)
81
+ name = "#{name}_passwords"
82
+ find_repo_item('clusters', ensure_json_extension(name)) || []
83
+ end
84
+
85
+ def find_repo_item(*args)
86
+ repo_names.each do |repo|
87
+ repo_args = args.dup
88
+ repo_item_dir_method = :"repo_#{repo_args.first}_dir"
89
+ repo_args[0] = respond_to?(repo_item_dir_method) ? send(repo_item_dir_method, repo) : repo_args.first
90
+ item_path = File.join(repo_dir(repo), repo_args)
91
+ if Dir.exists?(item_path) || File.exists?(item_path)
92
+ return item_path, repo
93
+ end
94
+ end
95
+ nil
96
+ end
97
+
98
+ def repo_cookbook_paths(dir_query='cookbooks')
99
+ @repo_cookbook_paths = {} unless @repo_cookbook_paths
100
+ unless @repo_cookbook_paths[dir_query]
101
+ @repo_cookbook_paths[dir_query] = []
102
+ repo_paths.each do |repo_dir|
103
+ thor.inside(repo_dir) do
104
+ Dir.glob('*').each do |f|
105
+ if f && File.directory?(f) && f =~ /#{dir_query}/
106
+ @repo_cookbook_paths[dir_query] << File.join(repo_dir, f)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ @repo_cookbook_paths[dir_query]
113
+ end
114
+
115
+ def get_cookbook_versions(options)
116
+ cookbook_versions = {}
117
+ get_cookbooks(options).each do |name|
118
+ version = get_cookbook_meta(name).version
119
+ cookbook_versions[name] = version
120
+ yield name, version if block_given?
121
+ end
122
+ cookbook_versions
123
+ end
124
+
125
+ def get_cookbooks(options, default=nil, paths=repo_cookbook_paths)
126
+ if options[:modified]
127
+ get_modified_cookbooks(options, paths)
128
+ else
129
+ options[:cookbooks] || (default || cookbook_loader(paths).cookbook_names)
130
+ end
131
+ end
132
+
133
+ def get_modified_cookbooks(options, paths=repo_cookbook_paths)
134
+ modified = []
135
+ cookbook_loader(paths).cookbook_names.each do |name|
136
+ cookbook_dir = cookbook_loader.cookbooks_by_name[name].root_dir
137
+ modified << name if modified?(cookbook_dir)
138
+ end
139
+ modified
140
+ end
141
+
142
+ def get_cookbook_meta(name, paths=repo_cookbook_paths)
143
+ cookbook_loader(paths).metadata[name]
144
+ end
145
+
146
+ def get_cookbook_deps(name, dependencies, options)
147
+ unless dependencies[name]
148
+ require_chef_deps
149
+ Dir.chdir(cookbook_loader.cookbooks_by_name[name].root_dir) do
150
+ ::Chef::Knife::Deps.load_deps
151
+ knife_deps = ::Chef::Knife::Deps.new(["."])
152
+ knife_deps.configure_chef
153
+ Chef::Config[:cookbook_path] = repo_cookbook_paths
154
+
155
+ entries = ::Chef::ChefFS::FileSystem.list(knife_deps.local_fs, ::Chef::ChefFS::FilePattern.new("/cookbooks/#{name}/."))
156
+ entries.each do |entry|
157
+ get_entry_dependencies(entry, dependencies, knife_deps, options)
158
+ end
159
+ end
160
+ end
161
+ dependencies
162
+ end
163
+
164
+ def get_entry_dependencies(entry, dependencies, knife_deps, options)
165
+ unless dependencies[entry.name]
166
+ cwd = Pathname.new(options[:cwd] || Dir.pwd)
167
+ dependencies[entry.name] = Pathname.new(entry.file_path).relative_path_from(cwd)
168
+ child_deps = knife_deps.get_dependencies(entry)
169
+ child_deps.each do |child|
170
+ child_entry = ::Chef::ChefFS::FileSystem.resolve_path(knife_deps.local_fs, child)
171
+ get_entry_dependencies(child_entry, dependencies, knife_deps, options)
172
+ end
173
+ end
174
+ end
175
+
176
+ def modified?(path, since='origin/master')
177
+ stat = Dir.chdir(path) do
178
+ `git diff #{since} -- #{path}`
179
+ end
180
+ !stat.empty?
181
+ end
182
+
183
+ def is_dirty?(file)
184
+ stat = Dir.chdir(File.dirname(file)) do
185
+ `git status #{file} --porcelain`
186
+ end
187
+ !stat.empty?
188
+ end
189
+
190
+ private
191
+
192
+ def cookbook_loader(paths=repo_cookbook_paths)
193
+ @cookbook_loader = {} unless @cookbook_loader
194
+ unless @cookbook_loader[paths]
195
+ require_chef_deps
196
+ @cookbook_loader[paths] = ::Chef::CookbookLoader.new(paths)
197
+ @cookbook_loader[paths].load_cookbooks
198
+ end
199
+ @cookbook_loader[paths]
200
+ end
201
+
202
+ def ensure_json_extension(item)
203
+ "#{item.split('.').first}.json"
204
+ end
205
+
206
+ def require_chef_deps
207
+ unless @chef_deps_loaded
208
+ require 'chef/knife'
209
+ require 'chef/knife/deps'
210
+ require 'chef/chef_fs/config'
211
+ require 'chef/cookbook_loader'
212
+ @chef_deps_loaded = true
213
+ end
214
+ end
215
+
216
+ end
217
+ end
@@ -0,0 +1,82 @@
1
+ require 'fhcap/tasks/task_base'
2
+ require 'chef'
3
+ require 'chef/knife'
4
+
5
+ module Fhcap
6
+ module Tasks
7
+ module Chef
8
+ class ChefTaskBase < TaskBase
9
+
10
+ def select_chef_server(environment, chef_server, fail_on_missing=true)
11
+ #ToDo Look at a better way of doing this rather than using a global variable
12
+ $selected_chef_server = {} unless $selected_chef_server
13
+ unless $selected_chef_server[environment]
14
+ if chef_server
15
+ $selected_chef_server[environment] = chef_server
16
+ else
17
+ chef_servers = chef_servers_for_environment(environment)
18
+ if chef_servers.empty?
19
+ exit_with_error("Unable to locate chef server for #{environment}") if fail_on_missing
20
+ elsif chef_servers.length == 1
21
+ $selected_chef_server[environment] = chef_servers.first
22
+ else
23
+ server = thor.ask("Multiple servers configured for environment '#{environment}'. Which one should be used?", {:limited_to => chef_servers})
24
+ $selected_chef_server[environment] = server
25
+ end
26
+ end
27
+ end
28
+ $selected_chef_server[environment]
29
+ end
30
+
31
+ def chef_servers_for_environment(environment)
32
+ chef_server_environments.collect do |server, environments|
33
+ server.to_s if environments.include? environment
34
+ end.compact
35
+ end
36
+
37
+ def chef_server_environments
38
+ unless @chef_server_environments
39
+ @chef_server_environments = {}
40
+ config[:knife].each do |name, cfg|
41
+ cmd = "environment list -F json"
42
+ resp = run_knife_cmd(cmd, name)
43
+ envs = JSON.parse(resp)
44
+ @chef_server_environments[name] = envs
45
+ end
46
+ end
47
+ @chef_server_environments
48
+ end
49
+
50
+ def knife_config_dir
51
+ config[:knife_dir] || File.join(config.default_dir, '.chef')
52
+ end
53
+
54
+ def knife_config_file_for(name)
55
+ File.join(knife_config_dir, "knife-#{name}.rb")
56
+ end
57
+
58
+ def run_knife_ssh_cmd(query, cmd, server, options)
59
+ user = options[:knife_user] || 'hadmin'
60
+ attr = options[:knife_attr] || 'cloud.public_ipv4'
61
+ knife_options = options[:knife_options]
62
+ run_knife_cmd("ssh \"#{query}\" \"#{cmd}\" -x #{user} -a #{attr} #{knife_options}", server, false)
63
+ end
64
+
65
+ def run_knife_cmd(cmd, server, capture=true)
66
+ cmd = "cd #{File.expand_path("..", knife_config_dir)} && knife #{cmd} -c #{knife_config_file_for(server)}"
67
+ resp = thor.run(cmd, :capture => capture)
68
+ thor.say resp if verbose && !capture
69
+ resp
70
+ end
71
+
72
+ def run_chef_client_cmd(cmd, server, capture=false)
73
+ cmd = "cd #{File.expand_path("..", knife_config_dir)} && chef-client -z #{cmd} -c #{knife_config_file_for(server)}"
74
+ resp = thor.run(cmd, :capture => capture)
75
+ thor.say resp if verbose && !capture
76
+ resp
77
+ end
78
+
79
+ end
80
+ end
81
+ end
82
+ end