puppet-library 0.10.0 → 0.11.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.yml +11 -0
  3. data/README.md +16 -7
  4. data/Rakefile +21 -21
  5. data/TODO.yml +10 -6
  6. data/bin/puppet-library +0 -1
  7. data/config.ru +14 -16
  8. data/features/step_definitions/sinatra_steps.rb +3 -12
  9. data/lib/puppet_library/forge.rb +1 -0
  10. data/lib/puppet_library/forge/abstract.rb +2 -1
  11. data/lib/puppet_library/forge/cache.rb +11 -7
  12. data/lib/puppet_library/forge/directory.rb +15 -13
  13. data/lib/puppet_library/forge/forge.rb +25 -0
  14. data/lib/puppet_library/forge/git_repository.rb +42 -17
  15. data/lib/puppet_library/forge/multi.rb +11 -1
  16. data/lib/puppet_library/forge/proxy.rb +14 -9
  17. data/lib/puppet_library/forge/source.rb +12 -13
  18. data/lib/puppet_library/http/cache/disk.rb +26 -8
  19. data/lib/puppet_library/http/cache/in_memory.rb +13 -10
  20. data/lib/puppet_library/http/cache/noop.rb +4 -1
  21. data/lib/puppet_library/puppet_library.rb +10 -7
  22. data/lib/puppet_library/puppet_module/modulefile.rb +5 -1
  23. data/lib/puppet_library/server.rb +13 -9
  24. data/lib/puppet_library/util.rb +1 -0
  25. data/lib/puppet_library/util/config_api.rb +115 -0
  26. data/lib/puppet_library/util/git.rb +36 -8
  27. data/lib/puppet_library/util/logging.rb +49 -0
  28. data/lib/puppet_library/util/patches.rb +21 -0
  29. data/lib/puppet_library/util/temp_dir.rb +20 -5
  30. data/lib/puppet_library/version.rb +1 -1
  31. data/puppet-library.gemspec +2 -0
  32. data/spec/archive/archive_reader_spec.rb +2 -6
  33. data/spec/archive/archiver_spec.rb +5 -9
  34. data/spec/forge/cache_spec.rb +13 -7
  35. data/spec/forge/directory_spec.rb +31 -18
  36. data/spec/forge/git_repository_spec.rb +60 -13
  37. data/spec/forge/multi_spec.rb +16 -0
  38. data/spec/forge/proxy_spec.rb +18 -0
  39. data/spec/forge/source_spec.rb +14 -9
  40. data/spec/http/cache/disk_spec.rb +11 -6
  41. data/spec/http/cache/in_memory_spec.rb +12 -0
  42. data/spec/http/cache/noop_spec.rb +6 -0
  43. data/spec/integration_test_helper.rb +3 -3
  44. data/spec/module_spec_helper.rb +2 -2
  45. data/spec/puppet_library_spec.rb +34 -16
  46. data/spec/puppet_module/modulefile_spec.rb +48 -9
  47. data/spec/server_spec.rb +28 -1
  48. data/spec/spec_helper.rb +2 -14
  49. data/spec/util/config_api_spec.rb +77 -0
  50. data/spec/util/git_spec.rb +20 -20
  51. data/spec/util/patches_spec.rb +18 -0
  52. data/test/directory_forge_integration_test.rb +6 -8
  53. data/test/offline_git_repo_forge_integration_test.rb +9 -14
  54. data/test/offline_proxy_forge_integration_test.rb +11 -14
  55. data/test/online_git_repo_forge_integration_test.rb +7 -8
  56. data/test/online_proxy_forge_integration_test.rb +10 -13
  57. data/test/source_forge_integration_test.rb +7 -8
  58. metadata +35 -2
@@ -15,7 +15,9 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
+ require 'puppet_library/forge/forge'
18
19
  require 'puppet_library/forge/search_result'
20
+ require 'puppet_library/util/patches'
19
21
 
20
22
  module PuppetLibrary::Forge
21
23
 
@@ -35,11 +37,19 @@ module PuppetLibrary::Forge
35
37
  # multi_forge = Multi.new
36
38
  # multi_forge.add_forge(Directory.new("/var/modules"))
37
39
  # multi_forge.add_forge(Proxy.new("http://forge.puppetlabs.com"))
38
- class Multi
40
+ class Multi < Forge
39
41
  def initialize
40
42
  @forges = []
41
43
  end
42
44
 
45
+ def prime
46
+ @forges.each_in_parallel &:prime
47
+ end
48
+
49
+ def clear_cache
50
+ @forges.each_in_parallel &:clear_cache
51
+ end
52
+
43
53
  # Add another forge to delegate to.
44
54
  def add_forge(forge)
45
55
  @forges << forge
@@ -15,9 +15,11 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
+ require 'puppet_library/forge/forge'
18
19
  require 'puppet_library/http/http_client'
19
20
  require 'puppet_library/http/cache/in_memory'
20
21
  require 'puppet_library/http/cache/noop'
22
+ require 'puppet_library/util/config_api'
21
23
 
22
24
  module PuppetLibrary::Forge
23
25
 
@@ -29,16 +31,14 @@ module PuppetLibrary::Forge
29
31
  # # The URL of the remote forge
30
32
  # repo.url = "http://forge.example.com
31
33
  # end
32
- class Proxy
34
+ class Proxy < Forge
33
35
 
34
- class Config
35
- attr_accessor :url
36
- end
37
-
38
- def self.configure
39
- config = Config.new
40
- yield(config)
41
- Proxy.new(config.url)
36
+ def self.configure(&block)
37
+ config_api = PuppetLibrary::Util::ConfigApi.for(Proxy) do
38
+ required :url, "URL"
39
+ end
40
+ config = config_api.configure(&block)
41
+ Proxy.new(config.get_url)
42
42
  end
43
43
 
44
44
  # * <tt>:url</tt> - The URL of the remote forge.
@@ -49,6 +49,11 @@ module PuppetLibrary::Forge
49
49
  @download_cache = download_cache
50
50
  end
51
51
 
52
+ def clear_cache
53
+ @query_cache.clear
54
+ @download_cache.clear
55
+ end
56
+
52
57
  def search_modules(query)
53
58
  query_parameter = query.nil? ? "" : "?q=#{query}"
54
59
  results = get("/modules.json#{query_parameter}")
@@ -27,14 +27,14 @@ module PuppetLibrary::Forge
27
27
  # <b>Note:</b>
28
28
  # The module directory must have a +Modulefile+.
29
29
  class Source < PuppetLibrary::Forge::Abstract
30
- class Config
31
- attr_accessor :path
32
- end
33
-
34
- def self.configure
35
- config = Config.new
36
- yield(config)
37
- Source.new(config.path)
30
+ def self.configure(&block)
31
+ config_api = PuppetLibrary::Util::ConfigApi.for(Source) do
32
+ required :path, "path to the module's source" do |path|
33
+ Dir.new(File.expand_path(path))
34
+ end
35
+ end
36
+ config = config_api.configure(&block)
37
+ Source.new(config.get_path)
38
38
  end
39
39
 
40
40
  CACHE_TTL_MILLIS = 500
@@ -42,16 +42,15 @@ module PuppetLibrary::Forge
42
42
  # * <tt>:module_dir</tt> - The directory containing the module's source.
43
43
  def initialize(module_dir)
44
44
  super(self)
45
- module_dir = File.expand_path(module_dir)
46
- raise "Module directory '#{module_dir}' doesn't exist" unless File.directory? module_dir
47
- raise "Module directory '#{module_dir}' isn't readable" unless File.executable? module_dir
45
+ raise "Module directory '#{module_dir.path}' doesn't exist" unless File.directory? module_dir.path
46
+ raise "Module directory '#{module_dir.path}' isn't readable" unless File.executable? module_dir.path
48
47
  @module_dir = module_dir
49
48
  @cache = PuppetLibrary::Http::Cache::InMemory.new(CACHE_TTL_MILLIS)
50
49
  end
51
50
 
52
51
  def get_module(author, name, version)
53
52
  return nil unless this_module?(author, name, version)
54
- PuppetLibrary::Archive::Archiver.archive_dir(@module_dir, "#{author}-#{name}-#{version}") do |archive|
53
+ PuppetLibrary::Archive::Archiver.archive_dir(@module_dir.path, "#{author}-#{name}-#{version}") do |archive|
55
54
  archive.add_file("metadata.json", 0644) do |entry|
56
55
  entry.write modulefile.to_metadata.to_json
57
56
  end
@@ -78,7 +77,7 @@ module PuppetLibrary::Forge
78
77
  end
79
78
 
80
79
  def modulefile
81
- modulefile_path = File.join(@module_dir, "Modulefile")
80
+ modulefile_path = File.join(@module_dir.path, "Modulefile")
82
81
  @cache.get modulefile_path do
83
82
  PuppetLibrary::PuppetModule::Modulefile.read(modulefile_path)
84
83
  end
@@ -15,14 +15,18 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
+ require 'fileutils'
19
+ require 'monitor'
20
+
18
21
  module PuppetLibrary::Http
19
22
  module Cache
20
23
  class Disk
21
24
  def initialize(directory)
22
25
  @directory = directory
26
+ @mutex = Monitor.new
23
27
  end
24
28
 
25
- def get(path)
29
+ def get(path = "entry")
26
30
  unless include? path
27
31
  buffer = yield
28
32
  save(path, buffer)
@@ -30,25 +34,39 @@ module PuppetLibrary::Http
30
34
  retrieve(path)
31
35
  end
32
36
 
37
+ def clear
38
+ @mutex.synchronize do
39
+ FileUtils.rm_rf @directory.path
40
+ end
41
+ end
42
+
33
43
  private
34
44
  def include?(path)
35
- File.exist? entry_path(path)
45
+ @mutex.synchronize do
46
+ File.exist? entry_path(path)
47
+ end
36
48
  end
37
49
 
38
50
  def save(path, buffer)
39
- file_path = entry_path(path)
40
- FileUtils.mkdir_p File.dirname(file_path)
41
- File.open(file_path, "w") do |file|
42
- file.write buffer.read
51
+ @mutex.synchronize do
52
+ file_path = entry_path(path)
53
+ FileUtils.mkdir_p File.dirname(file_path)
54
+ File.open(file_path, "w") do |file|
55
+ file.write buffer.read
56
+ end
43
57
  end
44
58
  end
45
59
 
46
60
  def retrieve(path)
47
- File.open(entry_path(path))
61
+ @mutex.synchronize do
62
+ File.open(entry_path(path))
63
+ end
48
64
  end
49
65
 
50
66
  def entry_path(path)
51
- File.join(@directory, path)
67
+ @mutex.synchronize do
68
+ File.join(@directory.path, path)
69
+ end
52
70
  end
53
71
  end
54
72
  end
@@ -18,12 +18,12 @@
18
18
  module PuppetLibrary::Http
19
19
  module Cache
20
20
  class InMemory
21
- ARBITRARY_CACHE_TTL_MILLIS = 10 * 1000
22
- def initialize(millis_to_live = ARBITRARY_CACHE_TTL_MILLIS)
23
- @reaper = Reaper.new(millis_to_live)
21
+ ARBITRARY_CACHE_TTL_SECONDS = 10
22
+ def initialize(seconds_to_live = ARBITRARY_CACHE_TTL_SECONDS)
23
+ @reaper = Reaper.new(seconds_to_live)
24
24
  end
25
25
 
26
- def get(key)
26
+ def get(key = "entry")
27
27
  entry = retrieve(key)
28
28
  if entry
29
29
  return entry.value unless @reaper.wants_to_kill? entry
@@ -42,6 +42,10 @@ module PuppetLibrary::Http
42
42
  cache[key] = entry
43
43
  end
44
44
 
45
+ def clear
46
+ @cache.clear
47
+ end
48
+
45
49
  private
46
50
  def cache
47
51
  @cache ||= {}
@@ -55,19 +59,18 @@ module PuppetLibrary::Http
55
59
  @value = value
56
60
  end
57
61
 
58
- def age_millis
59
- age_seconds = Time.now - @birth
60
- age_seconds * 1000
62
+ def age
63
+ Time.now - @birth
61
64
  end
62
65
  end
63
66
 
64
67
  class Reaper
65
- def initialize(millis_to_live)
66
- @millis_to_live = millis_to_live
68
+ def initialize(time_to_let_live)
69
+ @time_to_let_live = time_to_let_live
67
70
  end
68
71
 
69
72
  def wants_to_kill?(entry)
70
- entry.age_millis > @millis_to_live
73
+ entry.age > @time_to_let_live
71
74
  end
72
75
  end
73
76
  end
@@ -20,9 +20,12 @@ require 'puppet_library/http/cache/in_memory'
20
20
  module PuppetLibrary::Http
21
21
  module Cache
22
22
  class NoOp
23
- def get(path)
23
+ def get(path = "entry")
24
24
  yield
25
25
  end
26
+
27
+ def clear
28
+ end
26
29
  end
27
30
  end
28
31
  end
@@ -15,6 +15,7 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
+ require 'optparse'
18
19
  require 'rack'
19
20
  require 'yaml'
20
21
  require 'puppet_library/forge/directory'
@@ -103,10 +104,9 @@ module PuppetLibrary
103
104
  load_defaults!(options)
104
105
  process_options!(options)
105
106
 
106
- Server.configure do |server|
107
+ Server.configure do
107
108
  options[:forges].each do |(forge_type, config)|
108
- subforge = forge_type.new(*config)
109
- server.forge subforge
109
+ forge forge_type.new(*config)
110
110
  end
111
111
  end
112
112
  end
@@ -161,11 +161,14 @@ module PuppetLibrary
161
161
 
162
162
  def process_options!(options)
163
163
  options[:forges].map! do |(forge_type, config)|
164
- if forge_type == Forge::Proxy
165
- end
166
- if forge_type == Forge::Proxy && options[:cache_basedir]
164
+ if [ Forge::Directory, Forge::Source ].include? forge_type
165
+ [ forge_type, [ Dir.new(sanitize_path(config)) ]]
166
+ elsif forge_type == Forge::Proxy && options[:cache_basedir]
167
167
  cache_dir = File.join(options[:cache_basedir], url_hostname(config))
168
- [ Forge::Cache, [ config, sanitize_path(cache_dir) ] ]
168
+ path = sanitize_path(cache_dir)
169
+ FileUtils.mkdir_p path
170
+ dir = Dir.new(path)
171
+ [ Forge::Cache, [ config, dir ] ]
169
172
  else
170
173
  [ forge_type, config ]
171
174
  end
@@ -33,7 +33,7 @@ module PuppetLibrary::PuppetModule
33
33
  end
34
34
 
35
35
  def get_#{property}
36
- @#{property}
36
+ @#{property} || ""
37
37
  end
38
38
  EOF
39
39
  end
@@ -58,8 +58,12 @@ module PuppetLibrary::PuppetModule
58
58
  {
59
59
  "name" => get_name,
60
60
  "version" => get_version,
61
+ "source" => get_source,
61
62
  "author" => get_author,
63
+ "license" => get_license,
64
+ "summary" => get_summary,
62
65
  "description" => get_description,
66
+ "project_page" => get_project_page,
63
67
  "dependencies" => get_dependencies
64
68
  }
65
69
  end
@@ -17,8 +17,10 @@
17
17
 
18
18
  require 'sinatra/base'
19
19
  require 'haml'
20
+ require 'docile'
20
21
 
21
22
  require 'puppet_library/forge/multi'
23
+ require 'puppet_library/util/patches'
22
24
 
23
25
  module PuppetLibrary
24
26
  # The Puppet Library server
@@ -41,28 +43,26 @@ module PuppetLibrary
41
43
  end
42
44
 
43
45
  def forge(forge, &block)
44
- if forge.is_a? Class
45
- @forge.add_forge forge.configure(&block)
46
+ if forge.is_a? Symbol
47
+ class_name = forge.to_s.snake_case_to_camel_case
48
+ forge_type = Forge.module_eval(class_name)
49
+ @forge.add_forge forge_type.configure(&block)
46
50
  else
47
51
  @forge.add_forge forge
48
52
  end
49
53
  end
50
54
  end
51
55
 
52
- def self.set_up(&config_block)
53
- puts "PuppetLibrary::Server::set_up deprecated: use #configure instead"
54
- self.configure(&config_block)
55
- end
56
-
57
- def self.configure
56
+ def self.configure(&block)
58
57
  forge = Forge::Multi.new
59
- yield(Config.new(forge))
58
+ Docile.dsl_eval(Config.new(forge), &block)
60
59
  Server.new(forge)
61
60
  end
62
61
 
63
62
  def initialize(forge)
64
63
  super(nil)
65
64
  @forge = forge
65
+ @forge.prime
66
66
  end
67
67
 
68
68
  configure do
@@ -137,6 +137,10 @@ module PuppetLibrary
137
137
  end
138
138
  end
139
139
 
140
+ post "/api/forge/clear-cache" do
141
+ @forge.clear_cache
142
+ end
143
+
140
144
  private
141
145
  def download(buffer)
142
146
  if buffer.respond_to?(:size)
@@ -19,5 +19,6 @@ module PuppetLibrary::Util
19
19
  end
20
20
 
21
21
  require 'puppet_library/util/git'
22
+ require 'puppet_library/util/logging'
22
23
  require 'puppet_library/util/patches'
23
24
  require 'puppet_library/util/temp_dir'
@@ -0,0 +1,115 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Puppet Library
3
+ # Copyright (C) 2014 drrb
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'docile'
19
+
20
+ module PuppetLibrary::Util
21
+ class ConfigApi
22
+ def self.for(owner, &block)
23
+ Docile.dsl_eval(ConfigApi.new(owner), &block)
24
+ end
25
+
26
+ def initialize(owner)
27
+ @owner = owner
28
+ @params = []
29
+ end
30
+
31
+ def configure(&block)
32
+ config_api = config_class.new
33
+ Docile.dsl_eval(config_api, &block)
34
+ config_api.validate!
35
+ config_api
36
+ end
37
+
38
+ def required(name, description, &process)
39
+ param(name, description, true, process)
40
+ end
41
+
42
+ def param(name, description, required, process)
43
+ @params << Param.new(name, description, required, process)
44
+ end
45
+
46
+ private
47
+ def config_class
48
+ class_name = "#{@owner.to_s.split('::').last}Config"
49
+ if PuppetLibrary.const_defined?(class_name.intern)
50
+ PuppetLibrary.const_get(class_name)
51
+ else
52
+ define_config_class(class_name)
53
+ end
54
+ end
55
+
56
+ def define_config_class(class_name)
57
+ params = @params
58
+ config_class = Class.new(Config) do
59
+ define_method(:params) { params }
60
+ params.each do |param|
61
+ define_method(param.name.to_sym) do |new_value|
62
+ set(param, new_value)
63
+ end
64
+
65
+ define_method("get_#{param.name}".to_sym) do
66
+ get(param)
67
+ end
68
+ end
69
+ end
70
+ PuppetLibrary.const_set(class_name, config_class)
71
+ end
72
+
73
+ class Config
74
+ def initialize
75
+ @values = {}
76
+ end
77
+
78
+ def validate!
79
+ missing_params = params.select { |param| param.required? && @values[param].nil? }
80
+ unless missing_params.empty?
81
+ param = missing_params.first
82
+ raise "Config parameter '#{param.name}' is required (expected #{param.description}), but wasn't specified"
83
+ end
84
+ end
85
+
86
+ def get(param)
87
+ @values[param]
88
+ end
89
+
90
+ def set(param, new_value)
91
+ @values[param] = param.process(new_value)
92
+ rescue => validation_error
93
+ raise "Invalid value for config parameter '#{param.name}': #{validation_error.message} (was expecting #{param.description})"
94
+ end
95
+ end
96
+
97
+ class Param
98
+ attr_reader :name, :description
99
+
100
+ def initialize(name, description, required, process)
101
+ @name, @description, @required = name, description, required
102
+ do_nothing = lambda { |x| x }
103
+ @process = process || do_nothing
104
+ end
105
+
106
+ def required?
107
+ @required
108
+ end
109
+
110
+ def process(value)
111
+ @process.nil? || @process.call(value)
112
+ end
113
+ end
114
+ end
115
+ end