dtk-node-agent 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +18 -0
  4. data/README.md +42 -0
  5. data/bin/dtk-node-agent +16 -0
  6. data/dtk-node-agent.gemspec +38 -0
  7. data/lib/config/install.config +12 -0
  8. data/lib/dtk-node-agent/installer.rb +142 -0
  9. data/lib/dtk-node-agent/version.rb +3 -0
  10. data/mcollective_additions/plugins/README.md +1 -0
  11. data/mcollective_additions/plugins/v1.2/agent/discovery.rb +39 -0
  12. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.ddl +15 -0
  13. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.rb +79 -0
  14. data/mcollective_additions/plugins/v1.2/agent/git_access.ddl +9 -0
  15. data/mcollective_additions/plugins/v1.2/agent/git_access.rb +79 -0
  16. data/mcollective_additions/plugins/v1.2/agent/netstat.ddl +9 -0
  17. data/mcollective_additions/plugins/v1.2/agent/netstat.rb +34 -0
  18. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.ddl +9 -0
  19. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.rb +630 -0
  20. data/mcollective_additions/plugins/v1.2/agent/rpcutil.ddl +204 -0
  21. data/mcollective_additions/plugins/v1.2/agent/rpcutil.rb +101 -0
  22. data/mcollective_additions/plugins/v1.2/facts/pbuilder_facts.rb +35 -0
  23. data/mcollective_additions/plugins/v2.2/agent/dev_manager.ddl +9 -0
  24. data/mcollective_additions/plugins/v2.2/agent/dev_manager.rb +69 -0
  25. data/mcollective_additions/plugins/v2.2/agent/discovery.rb +39 -0
  26. data/mcollective_additions/plugins/v2.2/agent/dtk_node_agent_git_client.rb +94 -0
  27. data/mcollective_additions/plugins/v2.2/agent/execute_tests.ddl +9 -0
  28. data/mcollective_additions/plugins/v2.2/agent/execute_tests.rb +64 -0
  29. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.ddl +15 -0
  30. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.rb +79 -0
  31. data/mcollective_additions/plugins/v2.2/agent/git_access.ddl +9 -0
  32. data/mcollective_additions/plugins/v2.2/agent/git_access.rb +72 -0
  33. data/mcollective_additions/plugins/v2.2/agent/netstat.ddl +9 -0
  34. data/mcollective_additions/plugins/v2.2/agent/netstat.rb +34 -0
  35. data/mcollective_additions/plugins/v2.2/agent/ps.ddl +9 -0
  36. data/mcollective_additions/plugins/v2.2/agent/ps.rb +37 -0
  37. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.ddl +9 -0
  38. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.rb +633 -0
  39. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.ddl +10 -0
  40. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.rb +78 -0
  41. data/mcollective_additions/plugins/v2.2/agent/rpcutil.ddl +204 -0
  42. data/mcollective_additions/plugins/v2.2/agent/rpcutil.rb +101 -0
  43. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.ddl +10 -0
  44. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.rb +85 -0
  45. data/mcollective_additions/plugins/v2.2/agent/tail.ddl +11 -0
  46. data/mcollective_additions/plugins/v2.2/agent/tail.rb +67 -0
  47. data/mcollective_additions/plugins/v2.2/connector/r8stomp.rb +238 -0
  48. data/mcollective_additions/plugins/v2.2/connector/stomp.rb +349 -0
  49. data/mcollective_additions/plugins/v2.2/connector/stomp_em.rb +191 -0
  50. data/mcollective_additions/plugins/v2.2/facts/pbuilder_facts.rb +35 -0
  51. data/mcollective_additions/plugins/v2.2/security/sshkey.ddl +9 -0
  52. data/mcollective_additions/plugins/v2.2/security/sshkey.rb +362 -0
  53. data/mcollective_additions/server.cfg +22 -0
  54. data/puppet_additions/modules/r8/lib/puppet/type/r8_export_file.rb +53 -0
  55. data/puppet_additions/modules/r8/manifests/export_variable.rb +10 -0
  56. data/puppet_additions/puppet_lib_base/puppet/indirector/catalog/r8_storeconfig_backend.rb +48 -0
  57. data/puppet_additions/puppet_lib_base/puppet/indirector/r8_storeconfig_backend.rb +4 -0
  58. data/puppet_additions/puppet_lib_base/puppet/indirector/resource/r8_storeconfig_backend.rb +72 -0
  59. data/src/etc/init.d/ec2-run-user-data +95 -0
  60. data/src/etc/logrotate.d/mcollective +10 -0
  61. data/src/etc/logrotate.d/puppet +7 -0
  62. metadata +189 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MzkwMmViNTAyZjQwZTFmY2FiZTczODliMjE3YTQ0YTQ5YmU3OGIzYg==
5
+ data.tar.gz: !binary |-
6
+ ZGQ5NTFmZDZlZTc0MjViNTJjMzUzNDZhNzhiZGM0N2NhYjk3NGMxMQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YzdiYjNjYTM5ZjlmODJlNjBlMzFiMzdkMzEzOGZhYTVmNTc5NzdkNDBhZmE1
10
+ ODcwZWQ3ZTQ1MGI0NmRlMTU4NWZlYzY0ZjlmYTg1YmJjNTAzY2I4MGQ4Y2Rl
11
+ ZTE1YTI5ODg5ZTAzNzgyYThlOThiNTIwMjgyMGIzNWE5ZTljMDQ=
12
+ data.tar.gz: !binary |-
13
+ ZjUyNTU0NmNhYjYxYzJiMDExNzExNTg3M2EzMGM3N2FjNjMxNjc2MjMxZTkz
14
+ NTA5ZDFkZTU1MjcyNmZiOGM2MTAxZTJiZTdhNWJlNDlmYTExZDc1NTcxZmVl
15
+ NDQyODdkYzczMjViYzBkMDY1OTEwYzdmZTJhNjQwMTQ3OWJiYjI=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # dependcies are defined in gemspec file since to avoid duplication
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dtk-node-agent (0.1.0)
5
+ facter (~> 1.7.3)
6
+ trollop (~> 2.0)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ facter (1.7.3)
12
+ trollop (2.0)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ dtk-node-agent!
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ dtk-node-agent
2
+ ==============
3
+
4
+ Code that is present in AMIs that server basis for nodes being spun up
5
+
6
+ ### Build the gem:
7
+ `gem build dtk-node-agent.gemspec`
8
+
9
+ #### Intalling the node agent on a running machine (without puppet omnibus)
10
+ `sudo dtk-node-agent`
11
+
12
+ #### Intalling the node agent on a running machine (with puppet omnibus)
13
+ `sudo ./install_agent.sh`
14
+
15
+ #### Create a new AMI image with the node agent
16
+ ```
17
+ ./create_agent_ami.rb --help
18
+ Options:
19
+ --region, -r <s>: AWS Region on which to create the AMI image
20
+ --aws-key, -a <s>: AWS Access Key
21
+ --aws-secret, -w <s>: AWS Secret Access Key
22
+ --security-group, -s <s>: AWS Security group (default: default)
23
+ --key-pair, -k <s>: AWS keypair for the new instance
24
+ --key-path, -e <s>: Path to the PEM file for ssh access
25
+ --ssh-username, -u <s>: SSH Username
26
+ --ssh-timeout, -t <i>: Time to wait before instance is ssh ready (seconds) (default: 100)
27
+ --ami-id, -m <s>: AMI id which to spin up
28
+ --image-name, -i <s>: Name of the new image
29
+ --help, -h: Show this message
30
+ ```
31
+
32
+ example:
33
+ ```
34
+ ruby create_agent_ami.rb --region us-east-1 --ami-id ami-da0000aa --key-pair test_key --key-path /somepath/test_key.pem \
35
+ --ssh-username root --image-name dtk-agent-ubuntu-precise
36
+ ```
37
+
38
+ License
39
+ ----------------------
40
+ DTK Node Agent is released under the GPLv3 license. Please see LICENSE for more details.
41
+
42
+
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Bundler and rubygems maintain a set of directories from which to
4
+ # load gems. If Bundler is loaded, let it determine what can be
5
+ # loaded. If it's not loaded, then use rubygems. But do this before
6
+ # loading any dtk-node-agent code, so that our gem loading system is sane.
7
+ if not defined? ::Bundler
8
+ begin
9
+ require 'rubygems'
10
+ rescue LoadError
11
+ end
12
+ end
13
+
14
+ require 'dtk-node-agent/installer'
15
+
16
+ DTK::NodeAgent::Installer.run(ARGV)
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/dtk-node-agent/version', __FILE__)
3
+ prod_version_path = File.expand_path('../lib/dtk-node-agent/prod_version', __FILE__)
4
+ if File.exist?("#{prod_version_path}.rb")
5
+ require prod_version_path
6
+ else
7
+ DtkNodeAgent::PROD_VERSION = nil
8
+ end
9
+
10
+ Gem::Specification.new do |gem|
11
+ gem.authors = ["Rich PELAVIN"]
12
+ gem.email = ["rich@reactor8.com"]
13
+ gem.description = %q{DTK node agent is tool used to install and configure DTK agents.}
14
+ gem.summary = %q{DTK ndoe agent tool.}
15
+ gem.homepage = "https://github.com/rich-reactor8/dtk-node-agent"
16
+ gem.licenses = ["GPL-3.0"]
17
+
18
+ gem.files = %w(README.md Gemfile Gemfile.lock dtk-node-agent.gemspec)
19
+ gem.files += Dir.glob("bin/**/*")
20
+ gem.files += Dir.glob("lib/**/*")
21
+ gem.files += Dir.glob("puppet_additions/**/*")
22
+ gem.files += Dir.glob("mcollective_additions/**/*")
23
+ gem.files += Dir.glob("src/**/*")
24
+
25
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
27
+ gem.name = "dtk-node-agent"
28
+ gem.require_paths = ["lib"]
29
+ gem.version = DtkNodeAgent::PROD_VERSION || "#{DtkNodeAgent::VERSION}.#{ARGV[3]}".chomp(".")
30
+
31
+ gem.add_dependency 'puppet', '~> 3.3.2'
32
+ gem.add_dependency 'facter', '~> 1.7.3'
33
+ gem.add_dependency 'grit', '~> 2.5.0'
34
+ gem.add_dependency 'stomp', '~> 1.3.1'
35
+ gem.add_dependency 'sshkeyauth', '~> 0.0.11'
36
+ gem.add_dependency 'serverspec'
37
+
38
+ end
@@ -0,0 +1,12 @@
1
+ {
2
+ :puppet_version => '2.7.23',
3
+ :mcollective_version => 2.2,
4
+ :puppetlabs_el5_rpm_repo => 'http://yum.puppetlabs.com/el/5/products/i386/puppetlabs-release-5-7.noarch.rpm',
5
+ :puppetlabs_el6_rpm_repo => 'http://yum.puppetlabs.com/el/6/products/i386/puppetlabs-release-6-7.noarch.rpm',
6
+ :rpm_forge_el5_X86_64_repo => 'http://apt.sw.be/redhat/el5/en/x86_64/rpmforge/RPMS/rpmforge-release-0.5.3-1.el5.rf.x86_64.rpm',
7
+ :rpm_forge_el5_i686_repo => 'http://apt.sw.be/redhat/el5/en/i386/rpmforge/RPMS/rpmforge-release-0.5.3-1.el5.rf.i386.rpm',
8
+ :rpm_forge_el6_X86_64_repo => 'http://apt.sw.be/redhat/el6/en/x86_64/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm',
9
+ :rpm_forge_el6_i686_repo => 'http://apt.sw.be/redhat/el6/en/i386/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.i686.rpm',
10
+ :puppet_omnibus_deb64 => 'http://dtk-storage.s3.amazonaws.com/puppet-omnibus_2.7.23-fpm0_amd64.deb',
11
+ :puppet_omnibus_rpm64 => 'http://dtk-storage.s3.amazonaws.com/puppet-omnibus-2.7.23.fpm0-1.x86_64.rpm'
12
+ }
@@ -0,0 +1,142 @@
1
+ module DTK
2
+ module NodeAgent
3
+ class Installer
4
+
5
+ # read configuration
6
+ CONFIG = eval(File.open(File.expand_path('../config/install.config', File.dirname(__FILE__))) {|f| f.read })
7
+
8
+ def self.run(argv)
9
+ require 'optparse'
10
+ require 'facter'
11
+ require 'fileutils'
12
+ require 'dtk-node-agent/version'
13
+
14
+ @@options = parse(argv)
15
+
16
+ unless Process.uid == 0
17
+ puts "dtk-node-agent must be started with root/sudo privileges."
18
+ exit(1)
19
+ end
20
+
21
+ if Facter.operatingsystem == 'Debian' || Facter.operatingsystem == 'Ubuntu'
22
+ # set up apt and install packages
23
+ shell "apt-get update --fix-missing"
24
+ shell "apt-get install -y build-essential wget curl git"
25
+ shell "wget http://apt.puppetlabs.com/puppetlabs-release-#{Facter.lsbdistcodename}.deb"
26
+ puts "Installing Puppet Labs repository..."
27
+ shell "dpkg -i puppetlabs-release-#{Facter.lsbdistcodename}.deb"
28
+ shell "apt-get update"
29
+ shell "rm puppetlabs-release-#{Facter.lsbdistcodename}.deb"
30
+ # install mcollective
31
+ puts "Installing MCollective..."
32
+ shell "apt-get -y install mcollective"
33
+ elsif Facter.operatingsystem == 'CentOS' || Facter.operatingsystem == 'RedHat'
34
+ shell "yum -y install yum-utils wget"
35
+ case Facter.operatingsystemmajrelease
36
+ when "5"
37
+ shell "rpm -ivh #{CONFIG[:puppetlabs_el5_rpm_repo]}"
38
+ Facter.architecture == 'X86_64' ? (shell "rpm -ivh #{CONFIG[:rpm_forge_el5_X86_64_repo]}") : (shell "rpm -ivh #{CONFIG[:rpm_forge_el5_i686_repo]}")
39
+ when "6"
40
+ shell "rpm -ivh #{CONFIG[:puppetlabs_el6_rpm_repo]}"
41
+ Facter.architecture == 'X86_64' ? (shell "rpm -ivh #{CONFIG[:rpm_forge_el6_X86_64_repo]}") : (shell "rpm -ivh #{CONFIG[:rpm_forge_el6_i686_repo]}")
42
+ shell "yum-config-manager --disable rpmforge-release"
43
+ shell "yum-config-manager --enable rpmforge-extras"
44
+ else
45
+ puts "#{Facter.operatingsystem} #{Facter.operatingsystemmajrelease} is not supported. Exiting now..."
46
+ exit(1)
47
+ end
48
+ puts "Installing MCollective..."
49
+ shell "yum -y install mcollective"
50
+ shell "chkconfig mcollective on"
51
+ shell "yum -y install git"
52
+ # install ec2-run-user-data init script
53
+ FileUtils.cp("#{base_dir}/src/etc/init.d/ec2-run-user-data", "/etc/init.d/ec2-run-user-data") unless File.exist?("/etc/init.d/ec2-run-user-data")
54
+ shell "chmod +x /etc/init.d/ec2-run-user-data"
55
+ shell "chkconfig --level 345 ec2-run-user-data on"
56
+
57
+ else
58
+ echo "Unsuported OS for automatic agent installation. Exiting now..."
59
+ exit(1)
60
+ end
61
+
62
+ puts "Installing additions for MCollective and Puppet..."
63
+ install_additions
64
+
65
+ end
66
+
67
+
68
+ private
69
+
70
+ def self.parse(argv)
71
+ options = {}
72
+ parser = OptionParser.new do |opts|
73
+ opts.banner = <<-BANNER
74
+ usage:
75
+
76
+ dtk-node-agent [-p|--puppet-version] [-v|--version]
77
+ BANNER
78
+ opts.on("-d",
79
+ "--debug",
80
+ "enable debug mode") { |v| options[:debug] = true }
81
+ opts.on_tail("-v",
82
+ "--version",
83
+ "Print the version and exit.") do
84
+ puts ::DtkNodeAgent::VERSION
85
+ exit(0)
86
+ end
87
+ opts.on_tail("-h",
88
+ "--help",
89
+ "Print this help message.") do
90
+ puts parser
91
+ exit(0)
92
+ end
93
+ end
94
+
95
+ parser.parse!(argv)
96
+
97
+ options
98
+
99
+ rescue OptionParser::InvalidOption => e
100
+ $stderr.puts e.message
101
+ exit(12)
102
+ end
103
+
104
+ def self.shell(cmd)
105
+ puts "running: #{cmd}" if @@options[:debug]
106
+ output = `#{cmd}`
107
+ puts output if @@options[:debug]
108
+ if $?.exitstatus != 0
109
+ puts "Executing command \`#{cmd}\` failed"
110
+ puts "Command output:"
111
+ puts output
112
+ end
113
+ end
114
+
115
+ def self.install_additions
116
+ # create puppet group
117
+ shell "groupadd puppet" unless `grep puppet /etc/group`.include? "puppet"
118
+ # create necessary dirs
119
+ [ '/var/log/puppet/',
120
+ '/var/lib/puppet/lib/puppet/indirector',
121
+ '/etc/puppet/modules',
122
+ '/usr/share/mcollective/plugins/mcollective'
123
+ ].map! { |p| FileUtils.mkdir_p(p) unless File.directory?(p) }
124
+ # copy puppet libs
125
+ FileUtils.cp_r(Dir.glob("#{base_dir}/puppet_additions/puppet_lib_base/puppet/indirector/*"), "/var/lib/puppet/lib/puppet/indirector/")
126
+ # copy r8 puppet module
127
+ FileUtils.cp_r(Dir.glob("#{base_dir}/puppet_additions/modules/r8"), "/etc/puppet/modules")
128
+ # copy mcollective plugins
129
+ FileUtils.cp_r(Dir.glob("/usr/libexec/mcollective/mcollective/*"), "/usr/share/mcollective/plugins/mcollective") if File.directory?("/usr/libexec/mcollective/")
130
+ FileUtils.cp_r(Dir.glob("#{base_dir}/mcollective_additions/plugins/v#{CONFIG[:mcollective_version]}/*"), "/usr/share/mcollective/plugins/mcollective")
131
+
132
+ # copy mcollective config
133
+ FileUtils.cp_r("#{base_dir}/mcollective_additions/server.cfg", "/etc/mcollective", :remove_destination => true)
134
+ end
135
+
136
+ def self.base_dir
137
+ File.expand_path('../..', File.dirname(__FILE__))
138
+ end
139
+
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,3 @@
1
+ module DtkNodeAgent
2
+ VERSION="0.5.10"
3
+ end
@@ -0,0 +1 @@
1
+ Deprecate all mcolelctive agents in the os specfic directories
@@ -0,0 +1,39 @@
1
+ module MCollective
2
+ module Agent
3
+ # Discovery agent for The Marionette Collective
4
+ #
5
+ # Released under the Apache License, Version 2
6
+ class Discovery
7
+ attr_reader :timeout, :meta
8
+
9
+ def initialize
10
+ config = Config.instance.pluginconf
11
+
12
+ @timeout = 5
13
+ @timeout = config["discovery.timeout"].to_i if config.include?("discovery.timeout")
14
+
15
+ @meta = {:license => "Apache License, Version 2",
16
+ :author => "R.I.Pienaar <rip@devco.net>",
17
+ :timeout => @timeout,
18
+ :name => "Discovery Agent",
19
+ :version => MCollective.version,
20
+ :url => "http://www.marionette-collective.org",
21
+ :description => "MCollective Discovery Agent"}
22
+ end
23
+
24
+ def handlemsg(msg, stomp)
25
+ reply = "unknown request"
26
+
27
+ case msg[:body]
28
+ when "ping"
29
+ reply = "pong"
30
+
31
+ else
32
+ reply = "Unknown Request: #{msg[:body]}"
33
+ end
34
+
35
+ reply
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ metadata :name => "get log fragment",
2
+ :description => "get log fragment",
3
+ :author => "Reactor8",
4
+ :license => "",
5
+ :version => "",
6
+ :url => "",
7
+ :timeout => 20
8
+ action "get", :description => "get log data fragment" do
9
+ display :always
10
+ %w{status error data pbuilderid}.each do |k|
11
+ output k.to_sym,
12
+ :description => k.capitalize,
13
+ :display_as => k.capitalize
14
+ end
15
+ end
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+
4
+ module MCollective
5
+ #TODO: shoudl go in common area
6
+ LogFileHandles = {:task_id => Hash.new, :top_task_id => Hash.new}
7
+
8
+ module Agent
9
+ class Get_log_fragment < RPC::Agent
10
+ metadata :name => "get log fragment",
11
+ :description => "get log fragment",
12
+ :author => "Reactor8",
13
+ :license => "",
14
+ :version => "",
15
+ :url => "",
16
+ :timeout => 20
17
+ def initialize()
18
+ super()
19
+ @log = Log.instance
20
+ end
21
+ def get_action()
22
+ validate :key, String
23
+ validate :value, String
24
+ log_file_dir = "/var/log/puppet"
25
+ key = request[:key]
26
+ value = request[:value]
27
+ lines = get_log_fragment(log_file_dir,key,value)
28
+ pbuilderid = Facts["pbuilderid"]
29
+ if lines.nil?
30
+ error_msg = "Cannot find log fragment matching #{key}=#{value}"
31
+ error_response = {
32
+ :status => :failed,
33
+ :error => {
34
+ :message => error_msg
35
+ },
36
+ :pbuilderid => pbuilderid
37
+ }
38
+ @log.error(error_msg)
39
+ reply.data = error_response
40
+ else
41
+ ok_response = {
42
+ :status => :ok,
43
+ :data => lines,
44
+ :pbuilderid => pbuilderid
45
+ }
46
+ reply.data = ok_response
47
+ end
48
+ end
49
+ private
50
+ def get_log_fragment(log_file_dir,key,value)
51
+ #flush file if it is open
52
+ if file_handle = (LogFileHandles[key.to_sym]||{})[value]
53
+ file_handle.flush
54
+ end
55
+
56
+ delim = key.to_sym == :top_task_id ? "\\." : ":"
57
+ string_key = "#{key}:#{value.to_s}#{delim}"
58
+ ret = Array.new
59
+ matches = Dir["#{log_file_dir}/*.log"].grep(Regexp.new(string_key))
60
+ if matches.size == 0
61
+ return nil
62
+ end
63
+ matching_file = matches.size == 1 ?
64
+ matches.first :
65
+ #this finds teh most recent file
66
+ matches.map{|file|[file,File.mtime(file)]}.sort{|a,b|b[1]<=>a[1]}.first[0]
67
+ begin
68
+ f = File.open(matching_file)
69
+ until f.eof
70
+ ret << f.readline.chop
71
+ end
72
+ ensure
73
+ f.close
74
+ end
75
+ ret
76
+ end
77
+ end
78
+ end
79
+ end