chef-solr 0.9.18 → 0.10.0.beta.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+