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.
- data/bin/{chef-solr-indexer → chef-solr-installer} +4 -7
- data/lib/chef/solr.rb +1 -234
- data/lib/chef/solr/application/solr.rb +71 -43
- data/lib/chef/solr/solr_installer.rb +385 -0
- data/lib/chef/solr/version.rb +7 -1
- data/solr/solr-home.tar.gz +0 -0
- data/solr/solr-jetty.tar.gz +0 -0
- metadata +11 -41
- data/lib/chef/solr/application/indexer.rb +0 -148
- data/lib/chef/solr/index.rb +0 -103
- data/lib/chef/solr/index_queue_consumer.rb +0 -82
- data/lib/chef/solr/query.rb +0 -99
- data/spec/chef/solr/index_spec.rb +0 -187
- data/spec/chef/solr/query_spec.rb +0 -14
- data/spec/chef/solr_spec.rb +0 -301
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -14
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
# Author::
|
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
|
-
|
24
|
-
|
25
|
-
require 'chef/solr/application/indexer'
|
22
|
+
require 'chef/solr/solr_installer'
|
26
23
|
|
27
|
-
Chef::
|
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
|
-
|
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
|
+
|