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.
- checksums.yaml +4 -4
- data/CHANGELOG.yml +11 -0
- data/README.md +16 -7
- data/Rakefile +21 -21
- data/TODO.yml +10 -6
- data/bin/puppet-library +0 -1
- data/config.ru +14 -16
- data/features/step_definitions/sinatra_steps.rb +3 -12
- data/lib/puppet_library/forge.rb +1 -0
- data/lib/puppet_library/forge/abstract.rb +2 -1
- data/lib/puppet_library/forge/cache.rb +11 -7
- data/lib/puppet_library/forge/directory.rb +15 -13
- data/lib/puppet_library/forge/forge.rb +25 -0
- data/lib/puppet_library/forge/git_repository.rb +42 -17
- data/lib/puppet_library/forge/multi.rb +11 -1
- data/lib/puppet_library/forge/proxy.rb +14 -9
- data/lib/puppet_library/forge/source.rb +12 -13
- data/lib/puppet_library/http/cache/disk.rb +26 -8
- data/lib/puppet_library/http/cache/in_memory.rb +13 -10
- data/lib/puppet_library/http/cache/noop.rb +4 -1
- data/lib/puppet_library/puppet_library.rb +10 -7
- data/lib/puppet_library/puppet_module/modulefile.rb +5 -1
- data/lib/puppet_library/server.rb +13 -9
- data/lib/puppet_library/util.rb +1 -0
- data/lib/puppet_library/util/config_api.rb +115 -0
- data/lib/puppet_library/util/git.rb +36 -8
- data/lib/puppet_library/util/logging.rb +49 -0
- data/lib/puppet_library/util/patches.rb +21 -0
- data/lib/puppet_library/util/temp_dir.rb +20 -5
- data/lib/puppet_library/version.rb +1 -1
- data/puppet-library.gemspec +2 -0
- data/spec/archive/archive_reader_spec.rb +2 -6
- data/spec/archive/archiver_spec.rb +5 -9
- data/spec/forge/cache_spec.rb +13 -7
- data/spec/forge/directory_spec.rb +31 -18
- data/spec/forge/git_repository_spec.rb +60 -13
- data/spec/forge/multi_spec.rb +16 -0
- data/spec/forge/proxy_spec.rb +18 -0
- data/spec/forge/source_spec.rb +14 -9
- data/spec/http/cache/disk_spec.rb +11 -6
- data/spec/http/cache/in_memory_spec.rb +12 -0
- data/spec/http/cache/noop_spec.rb +6 -0
- data/spec/integration_test_helper.rb +3 -3
- data/spec/module_spec_helper.rb +2 -2
- data/spec/puppet_library_spec.rb +34 -16
- data/spec/puppet_module/modulefile_spec.rb +48 -9
- data/spec/server_spec.rb +28 -1
- data/spec/spec_helper.rb +2 -14
- data/spec/util/config_api_spec.rb +77 -0
- data/spec/util/git_spec.rb +20 -20
- data/spec/util/patches_spec.rb +18 -0
- data/test/directory_forge_integration_test.rb +6 -8
- data/test/offline_git_repo_forge_integration_test.rb +9 -14
- data/test/offline_proxy_forge_integration_test.rb +11 -14
- data/test/online_git_repo_forge_integration_test.rb +7 -8
- data/test/online_proxy_forge_integration_test.rb +10 -13
- data/test/source_forge_integration_test.rb +7 -8
- 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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
config
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
Source.new(config.
|
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
|
46
|
-
raise "Module 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
|
-
|
45
|
+
@mutex.synchronize do
|
46
|
+
File.exist? entry_path(path)
|
47
|
+
end
|
36
48
|
end
|
37
49
|
|
38
50
|
def save(path, buffer)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
61
|
+
@mutex.synchronize do
|
62
|
+
File.open(entry_path(path))
|
63
|
+
end
|
48
64
|
end
|
49
65
|
|
50
66
|
def entry_path(path)
|
51
|
-
|
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
|
-
|
22
|
-
def initialize(
|
23
|
-
@reaper = Reaper.new(
|
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
|
59
|
-
|
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(
|
66
|
-
@
|
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.
|
73
|
+
entry.age > @time_to_let_live
|
71
74
|
end
|
72
75
|
end
|
73
76
|
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
|
107
|
+
Server.configure do
|
107
108
|
options[:forges].each do |(forge_type, config)|
|
108
|
-
|
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
|
165
|
-
|
166
|
-
|
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
|
-
|
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?
|
45
|
-
|
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.
|
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
|
-
|
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)
|
data/lib/puppet_library/util.rb
CHANGED
@@ -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
|