chef-solr 0.9.18 → 0.10.0.beta.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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Author:: Adam Jacob (<adam@opscode.com>)
4
- # Copyright:: Copyright (c) 2009 Opscode, Inc.
3
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
4
+ # Copyright:: Copyright (c) 2009, 2011 Opscode, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,11 +18,8 @@
18
18
  #
19
19
 
20
20
  require 'rubygems'
21
-
22
21
  $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
23
- $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "chef", "lib")))
24
-
25
- require 'chef/solr/application/indexer'
22
+ require 'chef/solr/solr_installer'
26
23
 
27
- Chef::Solr::Application::Indexer.new.run
24
+ Chef::SolrInstaller.new(ARGV).run
28
25
 
data/lib/chef/solr.rb CHANGED
@@ -1,234 +1 @@
1
- #
2
- # Author:: Adam Jacob (<adam@opscode.com>)
3
- # Copyright:: Copyright (c) 2009 Opscode, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'chef/mixin/xml_escape'
20
- require 'chef/log'
21
- require 'chef/config'
22
- require 'chef/couchdb'
23
- require 'net/http'
24
- require 'libxml'
25
- require 'uri'
26
-
27
- class Chef
28
- class Solr
29
-
30
- include Chef::Mixin::XMLEscape
31
-
32
- attr_accessor :solr_url, :http
33
-
34
- def initialize(solr_url=Chef::Config[:solr_url])
35
- @solr_url = solr_url
36
- uri = URI.parse(@solr_url)
37
- @http = Net::HTTP.new(uri.host, uri.port)
38
- end
39
-
40
- def solr_select(database, type, options={})
41
- options[:wt] = :ruby
42
- options[:indent] = "off"
43
- options[:fq] = if type.kind_of?(Array)
44
- "+X_CHEF_database_CHEF_X:#{database} +X_CHEF_type_CHEF_X:#{type[0]} +data_bag:#{type[1]}"
45
- else
46
- "+X_CHEF_database_CHEF_X:#{database} +X_CHEF_type_CHEF_X:#{type}"
47
- end
48
- select_url = "/solr/select?#{to_params(options)}"
49
- Chef::Log.debug("Sending #{select_url} to Solr")
50
- req = Net::HTTP::Get.new(select_url)
51
-
52
- description = "Search Query to Solr '#{solr_url}#{select_url}'"
53
-
54
- res = http_request_handler(req, description)
55
- Chef::Log.debug("Parsing Solr result set:\n#{res.body}")
56
- eval(res.body)
57
- end
58
-
59
- def post_to_solr(doc)
60
- Chef::Log.debug("POSTing document to SOLR:\n#{doc}")
61
- req = Net::HTTP::Post.new("/solr/update", "Content-Type" => "text/xml")
62
- req.body = doc.to_s
63
-
64
- description = "POST to Solr '#{solr_url}'"
65
-
66
- http_request_handler(req, description)
67
- end
68
-
69
- START_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<add><doc>"
70
- END_XML = "</doc></add>\n"
71
- FIELD_ATTR = '<field name="'
72
- FIELD_ATTR_END = '">'
73
- CLOSE_FIELD = "</field>"
74
-
75
- def solr_add(data)
76
- Chef::Log.debug("adding to SOLR: #{data.inspect}")
77
-
78
- xml = ""
79
- xml << START_XML
80
-
81
- data.each do |field, values|
82
- values.each do |v|
83
- xml << FIELD_ATTR
84
- xml << xml_escape(field)
85
- xml << FIELD_ATTR_END
86
- xml << xml_escape(v)
87
- xml << CLOSE_FIELD
88
- end
89
- end
90
- xml << END_XML
91
- xml
92
-
93
- post_to_solr(xml)
94
- end
95
-
96
- def solr_commit(opts={})
97
- post_to_solr(generate_single_element("commit", opts))
98
- end
99
-
100
- def solr_optimize(opts={})
101
- post_to_solr(generate_single_element("optimize", opts))
102
- end
103
-
104
- def solr_rollback
105
- post_to_solr(generate_single_element("rollback"))
106
- end
107
-
108
- def solr_delete_by_id(ids)
109
- post_to_solr(generate_delete_document("id", ids))
110
- end
111
-
112
- def solr_delete_by_query(queries)
113
- post_to_solr(generate_delete_document("query", queries))
114
- end
115
-
116
- def rebuild_index(url=Chef::Config[:couchdb_url], db=Chef::Config[:couchdb_database])
117
- solr_delete_by_query("X_CHEF_database_CHEF_X:#{db}")
118
- solr_commit
119
-
120
- results = {}
121
- [Chef::ApiClient, Chef::Node, Chef::Role].each do |klass|
122
- results[klass.name] = reindex_all(klass) ? "success" : "failed"
123
- end
124
- databags = Chef::DataBag.cdb_list(true)
125
- Chef::Log.info("Reloading #{databags.size.to_s} #{Chef::DataBag} objects into the indexer")
126
- databags.each { |i| i.add_to_index; i.list(true).each { |x| x.add_to_index } }
127
- results[Chef::DataBag.name] = "success"
128
- results
129
- end
130
-
131
- private
132
-
133
- def reindex_all(klass, metadata={})
134
- begin
135
- items = klass.cdb_list(true)
136
- Chef::Log.info("Reloading #{items.size.to_s} #{klass.name} objects into the indexer")
137
- items.each { |i| i.add_to_index }
138
- rescue Net::HTTPServerException => e
139
- # 404s are okay, there might not be any of that kind of object...
140
- if e.message =~ /Not Found/
141
- Chef::Log.warn("Could not load #{klass.name} objects from couch for re-indexing (this is ok if you don't have any of these)")
142
- return false
143
- else
144
- raise e
145
- end
146
- rescue Exception => e
147
- Chef::Log.fatal("Chef encountered an error while attempting to load #{klass.name} objects back into the index")
148
- raise e
149
- end
150
- true
151
- end
152
-
153
- def generate_single_element(elem, opts={})
154
- xml_document = LibXML::XML::Document.new
155
- xml_elem = LibXML::XML::Node.new(elem)
156
- opts.each { |k,v| xml_elem[k.to_s] = xml_escape(v.to_s) }
157
- xml_document.root = xml_elem
158
- xml_document.to_s(:indent => false)
159
- end
160
-
161
- def generate_delete_document(type, list)
162
- list = [list] unless list.is_a?(Array)
163
- xml_document = LibXML::XML::Document.new
164
- xml_delete = LibXML::XML::Node.new("delete")
165
- xml_document.root = xml_delete
166
- list.each do |id|
167
- xml_id = LibXML::XML::Node.new(type)
168
- xml_id.content = id.to_s
169
- xml_delete << xml_id
170
- end
171
- xml_document.to_s(:indent => false)
172
- end
173
-
174
- # Thanks to Merb!
175
- def to_params(params_hash)
176
- params = ''
177
- stack = []
178
-
179
- params_hash.each do |k, v|
180
- if v.is_a?(Hash)
181
- stack << [k,v]
182
- else
183
- params << "#{k}=#{escape(v)}&"
184
- end
185
- end
186
-
187
- stack.each do |parent, hash|
188
- hash.each do |k, v|
189
- if v.is_a?(Hash)
190
- stack << ["#{parent}[#{k}]", escape(v)]
191
- else
192
- params << "#{parent}[#{k}]=#{escape(v)}&"
193
- end
194
- end
195
- end
196
-
197
- params.chop! # trailing &
198
- params
199
- end
200
-
201
- # escapes a query key/value for http
202
- # Thanks to RSolr!
203
- def escape(s)
204
- s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
205
- '%'+$1.unpack('H2'*$1.size).join('%').upcase
206
- }.tr(' ', '+')
207
- end
208
-
209
- # handles multiple net/http exceptions and no method closed? bug
210
- def http_request_handler(req, description='HTTP call')
211
- res = @http.request(req)
212
- unless res.kind_of?(Net::HTTPSuccess)
213
- Chef::Log.fatal("#{description} failed (#{res.class} #{res.code} #{res.message})")
214
- res.error!
215
- end
216
- res
217
- rescue Timeout::Error, Errno::EINVAL, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, NoMethodError => e
218
- # http://redmine.ruby-lang.org/issues/show/2708
219
- # http://redmine.ruby-lang.org/issues/show/2758
220
- if e.to_s =~ /#{Regexp.escape(%q|undefined method 'closed?' for nil:NilClass|)}/
221
- Chef::Log.fatal("#{description} failed. Chef::Exceptions::SolrConnectionError exception: Errno::ECONNREFUSED (net/http undefined method closed?) attempting to contact #{@solr_url}")
222
- Chef::Log.debug("rescued error in http connect, treating it as Errno::ECONNREFUSED to hide bug in net/http")
223
- Chef::Log.debug(e.backtrace.join("\n"))
224
- raise Chef::Exceptions::SolrConnectionError, "Errno::ECONNREFUSED: Connection refused attempting to contact #{@solr_url}"
225
- end
226
-
227
- Chef::Log.fatal("#{description} failed. Chef::Exceptions::SolrConnectionError exception: #{e.class.name}: #{e.to_s} attempting to contact #{@solr_url}")
228
- Chef::Log.debug(e.backtrace.join("\n"))
229
-
230
- raise Chef::Exceptions::SolrConnectionError, "#{e.class.name}: #{e.to_s}"
231
- end
232
-
233
- end
234
- end
1
+ require 'chef/solr/version'
@@ -15,12 +15,12 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require 'chef'
18
+
19
+ require 'rexml/document'
19
20
  require 'chef/log'
20
21
  require 'chef/config'
21
22
  require 'chef/application'
22
23
  require 'chef/daemon'
23
- require 'chef/client'
24
24
  require 'chef/solr'
25
25
 
26
26
  class Chef
@@ -116,7 +116,76 @@ class Chef
116
116
  super
117
117
  end
118
118
 
119
+ def schema_file_path
120
+ @schema_file_path ||= File.join(Chef::Config[:solr_home_path], 'conf', 'schema.xml')
121
+ end
122
+
123
+
124
+ def schema_document
125
+ @schema_document ||= begin
126
+ File.open(schema_file_path, 'r') do |xmlsux|
127
+ REXML::Document.new(xmlsux)
128
+ end
129
+ end
130
+ end
131
+
132
+ def schema_attributes
133
+ @schema_attributes ||= REXML::XPath.first(schema_document, '/schema').attributes
134
+ end
135
+
136
+ def solr_schema_name
137
+ schema_attributes["name"]
138
+ end
139
+
140
+ def solr_schema_version
141
+ schema_attributes["version"]
142
+ end
143
+
144
+ def valid_schema_name?
145
+ solr_schema_name == Chef::Solr::SCHEMA_NAME
146
+ end
147
+
148
+ def valid_schema_version?
149
+ solr_schema_version == Chef::Solr::SCHEMA_VERSION
150
+ end
151
+
152
+ def solr_home_exist?
153
+ File.directory?(Chef::Config[:solr_home_path])
154
+ end
155
+
156
+ def solr_data_dir_exist?
157
+ File.directory?(Chef::Config[:solr_data_path])
158
+ end
159
+
160
+ def solr_jetty_home_exist?
161
+ File.directory?(Chef::Config[:solr_jetty_path])
162
+ end
163
+
164
+ def assert_solr_installed!
165
+ unless solr_home_exist? && solr_data_dir_exist? && solr_jetty_home_exist?
166
+ Chef::Log.fatal "Chef Solr is not installed or solr_home_path, solr_data_path, and solr_jetty_path are misconfigured."
167
+ Chef::Log.fatal "Your current configuration is:"
168
+ Chef::Log.fatal "solr_home_path: #{Chef::Config[:solr_home_path]}"
169
+ Chef::Log.fatal "solr_data_path: #{Chef::Config[:solr_data_path]}"
170
+ Chef::Log.fatal "solr_jetty_path: #{Chef::Config[:solr_jetty_path]}"
171
+ Chef::Log.fatal "You can install Chef Solr using the chef-solr-installer script."
172
+ exit 1
173
+ end
174
+ end
175
+
176
+ def assert_valid_schema!
177
+ unless valid_schema_name? && valid_schema_version?
178
+ Chef::Log.fatal "Your Chef Solr installation needs to be upgraded."
179
+ Chef::Log.fatal "Expected schema version #{Chef::Solr::SCHEMA_VERSION} but version #{solr_schema_version} is installed."
180
+ Chef::Log.fatal "Use chef-solr-installer to upgrade your Solr install after backing up your data."
181
+ exit 1
182
+ end
183
+ end
184
+
119
185
  def setup_application
186
+ assert_solr_installed!
187
+ assert_valid_schema!
188
+
120
189
  # Need to redirect stdout and stderr so Java process inherits them.
121
190
  # If -L wasn't specified, Chef::Config[:log_location] will be an IO
122
191
  # object, otherwise it will be a String.
@@ -128,47 +197,7 @@ class Chef
128
197
 
129
198
  Chef::Log.level = Chef::Config[:log_level]
130
199
 
131
- # Build up a client
132
- node = Chef::Node.new
133
- node.platform = :default
134
- node.platform_version = 42
135
-
136
200
  Chef::Daemon.change_privilege
137
-
138
- solr_base = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "solr"))
139
-
140
- run_context = Chef::RunContext.new(node, {})
141
- # Create the Jetty container
142
- unless File.directory?(Chef::Config[:solr_jetty_path])
143
- Chef::Log.warn("Initializing the Jetty container")
144
- solr_jetty_dir = Chef::Resource::Directory.new(Chef::Config[:solr_jetty_path], run_context)
145
- solr_jetty_dir.recursive(true)
146
- solr_jetty_dir.run_action(:create)
147
- solr_jetty_untar = Chef::Resource::Execute.new("untar_jetty", run_context)
148
- solr_jetty_untar.command("tar zxvf #{File.join(solr_base, 'solr-jetty.tar.gz')}")
149
- solr_jetty_untar.cwd(Chef::Config[:solr_jetty_path])
150
- solr_jetty_untar.run_action(:run)
151
- end
152
-
153
- # Create the solr home
154
- unless File.directory?(Chef::Config[:solr_home_path])
155
- Chef::Log.warn("Initializing Solr home directory")
156
- solr_home_dir = Chef::Resource::Directory.new(Chef::Config[:solr_home_path], run_context)
157
- solr_home_dir.recursive(true)
158
- solr_home_dir.run_action(:create)
159
- solr_jetty_untar = Chef::Resource::Execute.new("untar_solr_home", run_context)
160
- solr_jetty_untar.command("tar zxvf #{File.join(solr_base, 'solr-home.tar.gz')}")
161
- solr_jetty_untar.cwd(Chef::Config[:solr_home_path])
162
- solr_jetty_untar.run_action(:run)
163
- end
164
-
165
- # Create the solr data path
166
- unless File.directory?(Chef::Config[:solr_data_path])
167
- Chef::Log.warn("Initializing Solr data directory")
168
- solr_data_dir = Chef::Resource::Directory.new(Chef::Config[:solr_data_path], run_context)
169
- solr_data_dir.recursive(true)
170
- solr_data_dir.run_action(:create)
171
- end
172
201
  end
173
202
 
174
203
  def run_application
@@ -191,7 +220,6 @@ class Chef
191
220
 
192
221
  STDOUT.reopen(@logfile)
193
222
  STDERR.reopen(@logfile)
194
- @logfile.close
195
223
  end
196
224
 
197
225
  Kernel.exec(command)
@@ -0,0 +1,385 @@
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@opscode.com)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'pp'
19
+ require 'optparse'
20
+ require 'chef/solr/version'
21
+ require 'chef/shell_out'
22
+ require 'chef/mixin/shell_out'
23
+
24
+ class Chef
25
+ class SolrInstaller
26
+
27
+ class Config
28
+ class CompatConfig
29
+ def initialize
30
+ @config_settings = {}
31
+ end
32
+
33
+ def from_file(file)
34
+ file = File.expand_path(file)
35
+ if File.readable?(file)
36
+ instance_eval(IO.read(file), file, 1)
37
+ else
38
+ STDERR.puts "Cannot open config file #{file} default settings will be used"
39
+ end
40
+ self
41
+ end
42
+
43
+ def method_missing(method_name, *args, &block)
44
+ if args.size == 1
45
+ @config_settings[method_name] = args.first
46
+ elsif args.empty?
47
+ @config_settings[method_name] or super
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def to_hash
54
+ @config_settings
55
+ end
56
+ end
57
+
58
+
59
+ def self.default_values
60
+ @default_values ||= {}
61
+ end
62
+
63
+ def self.configurables
64
+ @configurables ||= []
65
+ end
66
+
67
+ def self.configurable(value, default=nil)
68
+ configurables << value
69
+ attr_accessor value
70
+ default_values[value] = default if default
71
+ end
72
+
73
+ def each_configurable
74
+ self.class.configurables.each do |config_param|
75
+ yield [config_param, send(config_param)]
76
+ end
77
+ end
78
+
79
+ configurable :config_file, '/etc/chef/solr.rb'
80
+
81
+ # Defaults to /var/chef
82
+ configurable :solr_base_path, nil
83
+
84
+ def solr_base_path
85
+ @solr_base_path || '/var/chef'
86
+ end
87
+
88
+ # Sets the solr_base_path. Also resets solr_home_path, solr_jetty_path,
89
+ # and solr_data_path.
90
+ def solr_base_path=(base_path)
91
+ @solr_home_path, @solr_jetty_path, @solr_data_path = nil,nil,nil
92
+ @solr_base_path = base_path
93
+ end
94
+
95
+
96
+ # Computed from base path, defaults to /var/chef/solr
97
+ configurable :solr_home_path, nil
98
+
99
+ def solr_home_path
100
+ @solr_home_path || File.join(solr_base_path, 'solr')
101
+ end
102
+
103
+ # Computed from base path, defaults to /var/chef/solr-jetty
104
+ configurable :solr_jetty_path, nil
105
+
106
+ def solr_jetty_path
107
+ @solr_jetty_path || File.join(solr_base_path, 'solr-jetty')
108
+ end
109
+
110
+ # Computed from base path, defaults to /var/chef/solr/data
111
+ configurable :solr_data_path, nil
112
+
113
+ def solr_data_path
114
+ @solr_data_path || File.join(solr_base_path, 'solr', 'data')
115
+ end
116
+
117
+
118
+ configurable :user, nil
119
+
120
+ configurable :group, nil
121
+
122
+ configurable :force, false
123
+
124
+ alias :force? :force
125
+
126
+ configurable :noop, false
127
+
128
+ alias :noop? :noop
129
+
130
+ def initialize
131
+ apply_hash(self.class.default_values)
132
+ end
133
+
134
+ def configure_from(argv)
135
+ cli_config = CLI.parse_options(argv)
136
+ #pp :cli_config => cli_config.to_hash
137
+ config_file_config = CompatConfig.new.from_file(cli_config.config_file).to_hash
138
+ #pp :config_file_config => config_file_config
139
+ apply_hash(config_file_config)
140
+ apply_hash(cli_config.to_hash)
141
+ #pp :combined_config => self.to_hash
142
+ self
143
+ end
144
+
145
+ def to_hash
146
+ self.class.configurables.inject({}) do |hash, config_option|
147
+ value = instance_variable_get("@#{config_option}".to_sym)
148
+ hash[config_option] = value if value
149
+ hash
150
+ end
151
+ end
152
+
153
+ def apply_hash(hash)
154
+ hash.each do |key, value|
155
+ method_for_key = "#{key}=".to_sym
156
+ if respond_to?(method_for_key)
157
+ send(method_for_key, value)
158
+ else
159
+ STDERR.puts("Configuration setting #{key} is unknown and will be ignored")
160
+ end
161
+ end
162
+ end
163
+
164
+ module CLI
165
+ @config = Config.new
166
+
167
+ @option_parser = OptionParser.new do |o|
168
+ o.banner = "Usage: chef-solr-installer [options]"
169
+
170
+ o.on('-c', '--config CONFIG_FILE', 'The configuration file to use') do |conf|
171
+ @config.config_file = File.expand_path(conf)
172
+ end
173
+
174
+ o.on('-u', '--user USER', "User who will own Solr's data directory") do |u|
175
+ @config.user = u
176
+ end
177
+
178
+ o.on('-g', '--group GROUP', "Group that will own Solr's data directory") do |g|
179
+ @config.group = g
180
+ end
181
+
182
+ o.on('-p', '--base-path PATH', "The base path for the installation. Must be given before any -H -W or -D options") do |path|
183
+ @config.solr_base_path = path
184
+ end
185
+
186
+ o.on('-H', '--solr-home-dir PATH', 'Where to create the Solr home directory. Defaults to BASE_PATH/solr') do |path|
187
+ @config.solr_home_path = path
188
+ end
189
+
190
+ o.on('-W', '--solr-jetty-path PATH', 'Where to install Jetty for Solr. Defaults to BASE_PATH/solr-jetty ') do |path|
191
+ @config.solr_jetty_path = path
192
+ end
193
+
194
+ o.on('-D', '--solr-data-path PATH', 'Where to create the Solr data directory. Defaults to BASE_PATH/solr/data') do |path|
195
+ @config.solr_data_path = path
196
+ end
197
+
198
+ o.on('-n', '--noop', "Don't actually install, just show what would be done by the install") do
199
+ @config.noop = true
200
+ end
201
+
202
+ o.on('-f', '--force', 'Overwrite any existing installation without asking for confirmation') do
203
+ @config.force = true
204
+ end
205
+
206
+ o.on_tail('-h', '--help', 'show this message') do
207
+ puts "chef-solr-installer #{Chef::Solr::VERSION}"
208
+ puts ''
209
+ puts o
210
+ puts ''
211
+ puts 'Default Settings:'
212
+ @config.each_configurable do |param, value|
213
+ value_for_display = value || "none/false"
214
+ puts " #{param}:".ljust(20) + " #{value_for_display}"
215
+ end
216
+ exit 1
217
+ end
218
+
219
+ o.on_tail('-v', '--version', 'show the version and exit') do
220
+ puts "chef-solr-installer #{Chef::Solr::VERSION}"
221
+ exit 0
222
+ end
223
+
224
+ end
225
+
226
+ def self.parse_options(argv)
227
+ @option_parser.parse!(argv.dup)
228
+ @config
229
+ end
230
+
231
+ def self.config
232
+ @config
233
+ end
234
+
235
+ end
236
+
237
+ end
238
+
239
+ include Chef::Mixin::ShellOut
240
+
241
+ PACKAGED_SOLR_DIR = File.expand_path( "../../../../solr", __FILE__)
242
+
243
+ attr_reader :config
244
+
245
+ def initialize(argv)
246
+ @indent = 0
247
+ @config = Config.new.configure_from(argv.dup)
248
+ @overwriting = false
249
+ end
250
+
251
+ def overwriting?
252
+ @overwriting
253
+ end
254
+
255
+ def chef_solr_installed?
256
+ File.exist?(config.solr_home_path)
257
+ end
258
+
259
+ def run
260
+ say ''
261
+ say "*** DRY RUN ***" if config.noop?
262
+
263
+ if chef_solr_installed?
264
+ @overwriting = true
265
+ confirm_overwrite unless config.force? || config.noop?
266
+ scorch_the_earth
267
+ end
268
+
269
+ create_solr_home
270
+ create_solr_data_dir
271
+ unpack_solr_jetty
272
+
273
+ say ""
274
+ say "Successfully installed Chef Solr."
275
+
276
+ if overwriting?
277
+ say "You can restore your search index using `knife index rebuild`"
278
+ end
279
+ end
280
+
281
+ def confirm_overwrite
282
+ if STDIN.tty? && STDOUT.tty?
283
+ say "Chef Solr is already installed in #{config.solr_home_path}"
284
+ print "Do you want to overwrite the current install? All existing Solr data will be lost. [y/n] "
285
+ unless STDIN.gets =~ /^y/
286
+ say "Quitting. Try running this with --noop to see what it will change."
287
+ exit 1
288
+ end
289
+ else
290
+ say(<<-FAIL)
291
+ ERROR: Chef Solr is already installed in #{config.solr_home_path} and you did not use the
292
+ --force option. Use --force to overwrite an existing installation in a non-
293
+ interactive terminal.
294
+ FAIL
295
+ exit 1
296
+ end
297
+ end
298
+
299
+ def scorch_the_earth
300
+ group("Removing the existing Chef Solr installation") do
301
+ rm_rf(config.solr_home_path)
302
+ rm_rf(config.solr_jetty_path)
303
+ rm_rf(config.solr_data_path)
304
+ end
305
+ end
306
+
307
+ def create_solr_home
308
+ group("Creating Solr Home Directory") do
309
+ mkdir_p(config.solr_home_path)
310
+ chdir(config.solr_home_path) do
311
+ sh("tar zxvf #{File.join(PACKAGED_SOLR_DIR, 'solr-home.tar.gz')}")
312
+ end
313
+ end
314
+ end
315
+
316
+ def create_solr_data_dir
317
+ group("Creating Solr Data Directory") do
318
+ mkdir_p(config.solr_data_path)
319
+ chown(config.solr_data_path)
320
+ end
321
+ end
322
+
323
+ def unpack_solr_jetty
324
+ group("Unpacking Solr Jetty") do
325
+ mkdir_p(config.solr_jetty_path)
326
+ chdir(config.solr_jetty_path) do
327
+ sh("tar zxvf #{File.join(PACKAGED_SOLR_DIR, 'solr-jetty.tar.gz')}")
328
+ end
329
+ end
330
+ end
331
+
332
+ def mkdir_p(directory)
333
+ say "mkdir -p #{directory}"
334
+ FileUtils.mkdir_p(directory, :mode => 0755) unless config.noop?
335
+ end
336
+
337
+ def chdir(dir, &block)
338
+ say "entering #{dir}"
339
+ if config.noop?
340
+ yield if block_given? # still call the block so we get the noop output.
341
+ else
342
+ Dir.chdir(dir) { yield if block_given? }
343
+ end
344
+ end
345
+
346
+ def sh(*args)
347
+ opts = args[1, args.size - 1]
348
+ opts_msg = opts.empty? ? '' : " #{opts.to_s}"
349
+ say "#{args.first}#{opts_msg}"
350
+ shell_out!(*(args << {:cwd => false})) unless config.noop?
351
+ end
352
+
353
+ def chown(file)
354
+ if config.user
355
+ msg = "chown #{config.user}"
356
+ msg << ":#{config.group}" if config.group
357
+ msg << " #{file}"
358
+ say msg
359
+ FileUtils.chown(config.user, config.group) unless config.noop?
360
+ end
361
+ end
362
+
363
+ def rm_rf(path)
364
+ say "rm -rf #{path}"
365
+ FileUtils.rm_rf(path) unless config.noop?
366
+ end
367
+
368
+ def indent
369
+ @indent += 1
370
+ yield
371
+ @indent -= 1
372
+ end
373
+
374
+ def group(message, &block)
375
+ say(message)
376
+ indent(&block)
377
+ end
378
+
379
+ def say(message)
380
+ puts "#{' ' * (2 * @indent)}#{message}"
381
+ end
382
+
383
+ end
384
+ end
385
+