Src C0 Coverage Information - RCov

lib/model/instance.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
lib/model/instance.rb 533 435
82.93%
80.00%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 #
2 #   Copyright [2011] [Red Hat, Inc.]
3 #
4 #   Licensed under the Apache License, Version 2.0 (the "License");
5 #   you may not use this file except in compliance with the License.
6 #   You may obtain a copy of the License at
7 #
8 #   http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #   Unless required by applicable law or agreed to in writing, software
11 #   distributed under the License is distributed on an "AS IS" BASIS,
12 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #   See the License for the specific language governing permissions and
14 #  limitations under the License.
15 #
16 require 'fileutils'
17 require 'tmpdir'
18 require 'nokogiri'
19 require 'open-uri'
20 
21 require 'net/http'
22 require 'net/https'
23 
24 require 'active_support/ordered_hash'
25 
26 require 'zlib'
27 require 'archive/tar/minitar'
28 include Archive::Tar
29 
30 require 'lib/model/base'
31 
32 class Dir
33   class << self
34     @@dirstack = []
35     def pushd(dir, &block)
36       @@dirstack.unshift(Dir.pwd)
37       Dir.chdir(dir)
38       if block_given?
39         yield
40         popd
41       end
42       return @@dirstack
43     end
44     def popd
45       Dir.chdir(@@dirstack.shift) unless @@dirstack.empty?
46       return @@dirstack
47     end
48   end
49 end
50 
51 module ConfigServer
52   module Model
53     class InvalidInstanceConfigError < StandardError
54       attr_reader :errors
55       attr_reader :cause
56       attr_reader :message
57 
58       def initialize(errors=nil, cause=nil)
59         @errors = (errors.nil?) ? [] : errors
60         if not @errors.is_a? Array
61           @errors = [@errors]
62         end
63         @message = errors
64         @cause = cause if not cause.nil?
65       end
66 
67       def to_s
68         s = super()
69         @errors.each do |error|
70           s = "#{s}\n  Error: #{error}"
71         end
72         s
73       end
74     end
75 
76     def self.instance_config_schema_location=(location)
77       @intance_config_schema_location = location
78     end
79 
80     def self.instance_config_schema_location
81       @intance_config_schema_location
82     end
83 
84     class InvalidValidatorError < StandardError
85     end
86 
87     class Instance < Base
88       @@INSTANCE_CONFIG_FILE = "instance-config.xml"
89       @@PROVIDED_PARAMS_FILE = "provided-parameters.xml"
90       @@REQUIRED_PARAMS_FILE = "required-parameters.xml"
91       @@IP_FILE = "ip"
92       @@EMPTY_DOCUMENT = Nokogiri::XML("")
93 
94       # Nokogiri XML validator
95       @@validator = nil
96 
97       attr_reader :instance_config, :ip, :secret, :uuid
98 
99       def self.exists?(uuid)
100         File.exist?(storage_path uuid)
101       end
102 
103       def self.storage_path(uuid=nil)
104         path = uuid ? File.join("instances", uuid) : "instances"
105         super path
106       end
107 
108       def self.find(uuid)
109         Instance.new(uuid) if exists?(uuid)
110       end
111 
112       def self.delete!(uuid)
113         FileUtils.rm_rf(storage_path uuid)
114       end
115 
116       def self.get_validator
117         validator_schema = ConfigServer::Model.instance_config_schema_location
118         begin
119           @@validator ||= open(validator_schema) do |v|
120             Nokogiri::XML::RelaxNG(v)
121           end
122         rescue SocketError => se
123           raise InvalidValidatorError,
124             "Could not load validator from address #{validator_schema}"
125         rescue SystemCallError => sce
126           raise InvalidValidatorError,
127             "Could not load validator from file #{validator_schema}"
128         end
129         @@validator
130       end
131 
132       def self.validate(uuid, xml)
133         # make sure the xml is wrapped in Nokogiri
134         if xml.instance_of?(String) or xml.kind_of?(IO)
135           xml = Nokogiri::XML(xml)
136         end
137         errors = []
138         begin
139           errors = get_validator.validate(xml)
140         rescue Exception => e
141           raise InvalidInstanceConfigError.new(), ["The provided instance " +
142               "config for #{uuid} caused an error during validation:  ", e]
143         end
144         if errors.size > 0
145           raise InvalidInstanceConfigError.new(errors),
146                 "The provided instance config for #{uuid} is not a valid " +
147                 "instance-config document."
148         end
149         xml
150       end
151 
152       @uuid = ""
153       @secret = nil
154       @ip = ""
155       @instance_dir = ""
156 
157       def initialize(uuid, configs=nil)
158         super()
159         Instance.ensure_storage_path
160 
161         @uuid = uuid
162         @instance_dir = Instance.storage_path uuid
163         ensure_instance_dir
164 
165         if configs.nil?
166           load_configs
167         else
168           replace_instance_config(configs)
169         end
170         self
171       end
172 
173       def deployable
174         @deployable ||= Deployable.new(deployable_id)
175       end
176 
177       def assembly_name
178         @assembly_name ||=
179           (config % 'instance-config')['name'] if not config.nil?
180       end
181 
182       def instance_config=(xml)
183         replace_instance_config(xml)
184       end
185 
186       def ip=(ip)
187         replace_ip(ip)
188       end
189 
190       def provided_parameters_values=(params={})
191         logger.debug("provided params: #{params.inspect}")
192         if not (params.nil? or params.empty?)
193           params.each do |k,v|
194             param = pp % "//provided-parameter[@name='#{k}']"
195             param.inner_html = "<value><![CDATA[#{v}]]></value>" if not param.nil?
196           end
197           File.open(get_path(@@PROVIDED_PARAMS_FILE), 'w') do |f|
198             @provided_parameters.write_xml_to(f)
199           end
200         end
201       end
202 
203       def provided_parameters(opts={})
204         opts[:only_empty] ||= (not (opts[:only_with_values] || opts[:all]))
205         if opts[:raw]
206           return provided_parametrs_raw
207         end
208 
209         xpath = case
210           when opts[:only_empty]
211             '//provided-parameter[not(value)]'
212           when opts[:only_with_values]
213             '//provided-parameter/value/..'
214           else # opts[:all]
215             '//provided-parameter'
216         end
217 
218         if opts[:include_values]
219           #FIXME: only handles scalar values
220           (pp / xpath).map do |p|
221             {p['name'] => ((p%'value').nil? ? nil : (p%'value').content)}
222           end.inject(:merge)
223         else
224           (pp / xpath).map do |p|
225             p['name']
226           end
227         end
228       end
229 
230       def required_parameters_values=(params={})
231         logger.debug("required params: #{params.inspect}")
232         params.each do |k,v|
233           param = rp % "//required-parameter[@name='#{k}']"
234           logger.debug("param: #{param.to_xml}")
235           logger.debug("value: #{v}")
236           param.inner_html = "<value><![CDATA[#{v}]]></value>" if not param.nil?
237         end if not (params.nil? or params.empty?)
238         File.open(get_path(@@REQUIRED_PARAMS_FILE), 'w') do |f|
239           rp.write_xml_to(f)
240         end
241       end
242 
243       def required_parameters(opts={})
244         if opts[:raw]
245           return required_parameters_raw
246         end
247 
248         xpath = case
249           when opts[:only_empty]
250             '//required-parameter[not(value)]'
251           when opts[:only_with_values]
252             '//required-parameter/value/..'
253           else # opts[:all]
254             '//required-parameter'
255         end
256 
257         params = (rp / xpath).map do |p|
258           {:name      => p['name'],
259            :assembly  => p['assembly'],
260            :parameter => p['parameter']} +
261           if opts[:include_values]
262             #FIXME: only handles scalar values
263             v = p % 'value'
264             {:value   => (v.content if not v.nil?)}
265           else
266             {}
267           end
268         end
269       end
270 
271       def services
272         services = ActiveSupport::OrderedHash.new
273         (config / '//service').each do |s|
274           name = s["name"]
275           params_with_values = (s / './parameters/parameter/value/..') +
276             (rp / "//required-parameter[@service='#{name}']/value/..")
277           parameters = (params_with_values.map do |p|
278             {p["name"] => (p % 'value').content}
279           end || []).inject(:merge) || {}
280           services[name] = parameters
281         end
282         services
283       end
284 
285       def required_parameters_remaining?
286         (rp / "//required-parameter[not(value)]").size > 0
287       end
288 
289       def has_file?
290         File.exists?(get_file)
291       end
292 
293       def file
294         path = get_path("#{@uuid}.tgz")
295         return (File.exists?(path)) ? path : nil
296       end
297 
298       def file=(file)
299         if file.nil?
300           return nil
301         else
302           path = get_path("#{@uuid}.tgz")
303           File.open(path, "wb") do |f|
304             f.write(file[:tempfile].read)
305           end
306           return path
307         end
308       end
309 
310       private
311       alias config instance_config
312 
313       def required_parameters_raw
314         @required_parameters || @@EMPTY_DOCUMENT
315       end
316       alias rp required_parameters_raw
317 
318       def provided_parameters_raw
319         @provided_parameters || @@EMPTY_DOCUMENT
320       end
321       alias pp provided_parameters_raw
322 
323       def ensure_instance_dir
324         FileUtils.mkdir_p(@instance_dir, :mode => 0700) if not File.directory?(@instance_dir)
325       end
326 
327       def load_configs
328         @instance_config = get_xml(get_path(@@INSTANCE_CONFIG_FILE))
329         @provided_parameters = get_xml(get_path(@@PROVIDED_PARAMS_FILE))
330         @required_parameters = get_xml(get_path(@@REQUIRED_PARAMS_FILE))
331         @ip = get_ip
332         deployable
333       end
334 
335       def get_path(filename)
336         File.join(@instance_dir, filename)
337       end
338 
339       def get_xml(filename)
340         File.open(filename) do |f|
341           Nokogiri::XML(f)
342         end if File.exists?(filename)
343       end
344 
345       def deployable_id
346         (config % '//deployable')['id'] if not config.nil?
347       end
348 
349       def replace_instance_config(xml)
350         xml = Instance.validate(@uuid, xml)
351         file = get_path(@@INSTANCE_CONFIG_FILE)
352         File.open(file, "w") do |f|
353           xml.write_xml_to(f)
354         end
355         @instance_config = xml
356 
357         replace_provided_parameters
358         replace_required_parameters
359 
360         replace_tarball
361 
362         @secret = get_secret
363 
364         deployable
365         @deployable.add_instance(@uuid)
366 
367         @instance_config
368       end
369 
370       def get_ip
371         filename = get_path(@@IP_FILE)
372         File.open(filename) do |f|
373           f.read
374         end if File.exists?(filename)
375       end
376 
377       def get_secret
378         config.root['secret']
379       end
380 
381       def replace_ip(ip)
382         file = get_path(@@IP_FILE)
383         File.open(file, 'w') {|f| f.write(ip) }
384       end
385 
386       def replace_provided_parameters
387         provided_params = config / '//provided-parameter'
388         xml = ""
389         if not provided_params.empty?
390           xml = "<provided-parameters>\n"
391           provided_params.each do |p|
392             xml += "  <provided-parameter name='#{p['name']}'/>\n"
393           end
394           xml += "</provided-parameters>\n"
395         end
396         # Always create the provided_params file, even
397         # if it's empty...saves a little trouble later
398         # on with some validation
399         file = get_path(@@PROVIDED_PARAMS_FILE)
400         File.open(file, 'w') do |f|
401           f.write(xml)
402         end
403         @provided_parameters = Nokogiri::XML(xml)
404       end
405 
406       def replace_required_parameters
407         file = get_path(@@REQUIRED_PARAMS_FILE)
408         # grab all the services with reference parameters
409         services = config / '//service/parameters/parameter/reference/../../..'
410         if not services.empty?
411           xml = "<required-parameters>\n"
412           services.each do |s|
413             xml += "  <required-parameter service='#{s['name']}'"
414             (services / './parameters/parameter/reference/..').each do |p|
415               xml += " name='#{p['name']}'"
416               ref = p % 'reference'
417               xml += " assembly='#{ref['assembly']}'"
418               xml += " parameter='#{ref['provided-parameter']}'/>\n"
419             end
420           end
421           xml += "</required-parameters>\n"
422           logger.debug("reqparams xml: #{xml}")
423 
424           File.open(file, 'w') do |f|
425             f.write(xml)
426           end
427           @required_parameters = Nokogiri::XML(xml)
428         else
429           File.delete(file) if File.exists?(file)
430           @required_parameters = nil
431         end
432       end
433 
434       def replace_tarball
435         # mk a tmpdir
436         Dir.mktmpdir do |dir|
437           # grab all the executable files and conf files
438           get_configuration_scripts(dir, :type => :executable)
439           get_configuration_scripts(dir, :type => :file)
440 
441           # tar the contents of the tmpdir
442           Dir.pushd(dir) do
443             tar = File.open(get_path("#{@uuid}.tgz"), 'wb') do |f|
444               tgz = Zlib::GzipWriter.new(f)
445               Minitar.pack('.', tgz)
446             end
447           end
448         # auto-unlinks the tmpdir
449         end
450       end
451 
452       def get_configuration_scripts(dir, opts={})
453         opts[:type] ||= :executable
454         config_type = opts[:type]
455         if not [:executable, :file].include? config_type
456           raise RuntimeError, "unknown configuration file type #{config_type}"
457         end
458         (config / config_type.to_s).each do |node|
459           config_dir = dir
460           parent = (:file == config_type) ? node.parent.parent : node.parent
461           if "service" == parent.name
462             svc_name = parent['name']
463             config_dir = "#{dir}/#{svc_name}"
464             Dir.mkdir config_dir if not File.exists? config_dir
465           end
466           # if file is URL, download file
467           # else read file from cdata contents
468           begin
469             file_data = get_configuration_file(node)
470             write_configuration_file(file_data, config_dir, opts[:type])
471           rescue => e
472             puts "ERROR: could not get configuration file contents"
473             puts e
474           end
475 
476         end
477       end
478 
479       def get_configuration_file(node)
480         file_data = {}
481         if not node['url']
482           file_content_node = node.first_element_child
483           if file_content_node.nil?
484             raise "No 'contents' element found for configuration file without url: #{node.name}"
485           end
486           file_data[:name] = file_content_node[:filename]
487           file_data[:body] = file_content_node.content
488         else
489           file_data = download_file(node['url'])
490           if file_data[:code] != "200"
491             raise "Could not download file #{node['url']}.  Http Response code: #{file_data[:code]}"
492           end
493         end
494         file_data
495       end
496 
497       def write_configuration_file(file_data, config_dir, file_type=:executable)
498         filename = (:file == file_type) ? file_data[:name] : "start"
499         if file_type == :executable
500           open("#{config_dir}/#{filename}", "wb", 0777) do |file|
501             file << file_data[:body]
502           end
503         else
504           open("#{config_dir}/#{filename}", "wb") do |file|
505             file << file_data[:body]
506           end
507         end
508       end
509 
510       def download_file(url)
511         result = {}
512         uri = URI.parse(url)
513         http = Net::HTTP.new(uri.host, uri.port)
514         http.use_ssl = uri.port == 443
515         request = Net::HTTP::Get.new(uri.path)
516         response = http.start {|h| h.request(request) }
517         result[:code] = response.code
518         if "200" == response.code.to_s
519           result[:name] = uri.path.split('/').last
520           result[:body] = response.body
521         end
522         result
523       end
524 
525       def pending_required_params?(service=nil)
526         xpath = (service.nil?) ?
527           '//required-parameter[not(value)]' :
528           "//required-parameter[@service='#{service}'][not(value)]"
529         not (rp.nil? or (rp / xpath).empty?)
530       end
531     end
532   end
533 end

Generated on Wed Dec 14 16:00:31 -0500 2011 with rcov 0.9.11