puppet-library 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/README.md +23 -5
- data/Rakefile +18 -4
- data/TODO.yml +14 -3
- data/config.ru +7 -3
- data/lib/puppet_library.rb +5 -1
- data/lib/puppet_library/forge.rb +100 -0
- data/lib/puppet_library/http/cache.rb +61 -0
- data/lib/puppet_library/http/http_client.rb +35 -0
- data/lib/puppet_library/http/url.rb +27 -0
- data/lib/puppet_library/module_metadata.rb +9 -4
- data/lib/puppet_library/module_repo/proxy.rb +87 -0
- data/lib/puppet_library/puppet_library.rb +30 -20
- data/lib/puppet_library/server.rb +10 -67
- data/lib/puppet_library/util.rb +2 -2
- data/lib/puppet_library/version.rb +1 -1
- data/puppet-library.gemspec +3 -1
- data/spec/forge_spec.rb +147 -0
- data/spec/http/cache_spec.rb +63 -0
- data/spec/http/http_client_spec.rb +44 -0
- data/spec/http/url_spec.rb +39 -0
- data/spec/module_repo/directory_spec.rb +9 -5
- data/spec/module_repo/proxy_spec.rb +105 -0
- data/spec/module_spec_helper.rb +3 -1
- data/spec/puppet_library_spec.rb +64 -15
- data/spec/server_spec.rb +45 -77
- data/spec/spec_helper.rb +2 -0
- data/test/librarian_puppet_integration_test.rb +111 -0
- metadata +47 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd852d5afc9f767f3adbd20e96a3d6ba37fec092
|
4
|
+
data.tar.gz: 9030ad628af928e81289963f28e9df751a4240b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a168548b6050b5d34d121b1191f74dfe5a84e11024714f07d753828ef72894ef3b039ffbbd8c3c7a125179b29c4862ea8d4de17f2c45a3e0cf60527d48ae723a
|
7
|
+
data.tar.gz: 1c130b6732c99488fffc95442bca7b3604b0ae38213aba4415b185dd79514b705ee76cdc8710f1b53054f5428c54143b54ae80ec987c7a243888f51752602c5f
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
# Puppet Library
|
2
2
|
|
3
|
-
A server for your Puppet modules. Compatible with [librarian-puppet](http://librarian-puppet.com).
|
3
|
+
A private server for your Puppet modules. Compatible with [librarian-puppet](http://librarian-puppet.com).
|
4
4
|
|
5
5
|
[![Build Status](https://travis-ci.org/drrb/puppet-library.png?branch=master)](https://travis-ci.org/drrb/puppet-library)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/drrb/puppet-library/badge.png)](https://coveralls.io/r/drrb/puppet-library)
|
7
|
+
[![Code Climate](https://codeclimate.com/github/drrb/puppet-library.png)](https://codeclimate.com/github/drrb/puppet-library)
|
7
8
|
|
8
|
-
|
9
|
+
[![Gem Version](https://badge.fury.io/rb/puppet-library.png)](http://badge.fury.io/rb/puppet-library)
|
10
|
+
[![Dependency Status](https://gemnasium.com/drrb/puppet-library.png)](https://gemnasium.com/drrb/puppet-library)
|
11
|
+
|
12
|
+
Puppet Library serves Puppet modules in the same format as [the Puppet Forge](http://forge.puppetlabs.com). This allows you to create a private Puppet Forge and manage all the modules you use completely within your infrastructure.
|
13
|
+
|
14
|
+
Plugins can be created to serve modules from arbitrary sources. Puppet Library contains built-in support for:
|
15
|
+
- serving packaged (`.tar.gz`) modules from a directory (or directories) of your choosing
|
16
|
+
- proxying a remote forge (or forges)
|
9
17
|
|
10
18
|
## Installation
|
11
19
|
|
@@ -13,6 +21,12 @@ Install the server as a Gem:
|
|
13
21
|
|
14
22
|
$ gem install puppet-library
|
15
23
|
|
24
|
+
Or, to get the latest, you can install from source
|
25
|
+
|
26
|
+
$ git clone https://github.com/drrb/puppet-library.git
|
27
|
+
$ cd puppet-library
|
28
|
+
$ rake install
|
29
|
+
|
16
30
|
## Getting Started
|
17
31
|
|
18
32
|
Create a module directory and add the modules to it
|
@@ -24,7 +38,7 @@ Create a module directory and add the modules to it
|
|
24
38
|
|
25
39
|
Start the server
|
26
40
|
|
27
|
-
$ puppet-library
|
41
|
+
$ puppet-library --port 9292 --module-dir ./modules
|
28
42
|
|
29
43
|
Point librarian-puppet to the server
|
30
44
|
|
@@ -40,7 +54,7 @@ Resolve and download the modules
|
|
40
54
|
|
41
55
|
## Usage
|
42
56
|
|
43
|
-
Run the server
|
57
|
+
Run the server (proxies the Puppet Forge by default)
|
44
58
|
|
45
59
|
$ puppet-library
|
46
60
|
|
@@ -48,6 +62,10 @@ Serve modules from a specific directory
|
|
48
62
|
|
49
63
|
$ puppet-library --module-dir /var/puppet-modules
|
50
64
|
|
65
|
+
Serve modules from a remote forge as a proxy
|
66
|
+
|
67
|
+
$ puppet-library --proxy http://forge.puppetlabs.com
|
68
|
+
|
51
69
|
Serve modules on a specific port
|
52
70
|
|
53
71
|
$ puppet-library --port 8888
|
@@ -73,7 +91,7 @@ This project was inspired by [dalen/simple-puppet-forge](https://github.com/dale
|
|
73
91
|
## License
|
74
92
|
|
75
93
|
Puppet Library
|
76
|
-
Copyright (C)
|
94
|
+
Copyright (C) 2014 drrb
|
77
95
|
|
78
96
|
This program is free software: you can redistribute it and/or modify
|
79
97
|
it under the terms of the GNU General Public License as published by
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# Puppet Library
|
3
|
-
# Copyright (C)
|
3
|
+
# Copyright (C) 2014 drrb
|
4
4
|
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
6
|
# it under the terms of the GNU General Public License as published by
|
@@ -19,14 +19,28 @@ require 'bundler/gem_tasks'
|
|
19
19
|
require 'rspec/core/rake_task'
|
20
20
|
require 'coveralls/rake/task'
|
21
21
|
|
22
|
-
|
22
|
+
if RUBY_VERSION.start_with? "1.8"
|
23
|
+
# The integration test doesn't work on Ruby 1.8.
|
24
|
+
DEFAULT_TEST_TASK = :spec
|
25
|
+
else
|
26
|
+
DEFAULT_TEST_TASK = :test
|
27
|
+
end
|
28
|
+
|
23
29
|
Coveralls::RakeTask.new
|
24
30
|
|
25
|
-
|
31
|
+
desc "Run the specs"
|
32
|
+
RSpec::Core::RakeTask.new(:spec)
|
33
|
+
|
34
|
+
desc "Run all the tests"
|
35
|
+
RSpec::Core::RakeTask.new(:test) do |rspec|
|
36
|
+
rspec.pattern = "{spec,test}/**/*_{spec,integration_test}.rb"
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => [DEFAULT_TEST_TASK, 'coveralls:push']
|
26
40
|
|
27
41
|
desc "Check it works on all local rubies"
|
28
42
|
task :verify do
|
29
|
-
system "rvm all do rake
|
43
|
+
system "rvm all do rake test"
|
30
44
|
end
|
31
45
|
|
32
46
|
desc "Check all files for license headers"
|
data/TODO.yml
CHANGED
@@ -1,5 +1,16 @@
|
|
1
|
-
|
2
|
-
- Configure embedded server (currently uses the default Rack server)
|
3
|
-
- Proxy another repository
|
1
|
+
features:
|
4
2
|
- Use config from a config file
|
3
|
+
- Allow proxy to cache downloaded modules/API calls on disk
|
4
|
+
- |
|
5
|
+
Accept --cache-dir option to specify a parent cache directory (people
|
6
|
+
will need to embed the server or use a config file for more control)
|
7
|
+
- Validate repo dirs (make sure they're readable)
|
8
|
+
- Allow failover between module repositories
|
5
9
|
- Document usage with Passenger
|
10
|
+
- Web UI
|
11
|
+
- Authentication
|
12
|
+
|
13
|
+
dubious_features:
|
14
|
+
- Make proxy cache TTL configurable
|
15
|
+
- REST API to clear proxy cache
|
16
|
+
- REST API to upload modules
|
data/config.ru
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# Puppet Library
|
3
|
-
# Copyright (C)
|
3
|
+
# Copyright (C) 2014 drrb
|
4
4
|
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
6
|
# it under the terms of the GNU General Public License as published by
|
@@ -18,5 +18,9 @@
|
|
18
18
|
require 'rubygems'
|
19
19
|
require 'puppet_library'
|
20
20
|
|
21
|
-
PuppetLibrary::Server.
|
22
|
-
|
21
|
+
server = PuppetLibrary::Server.set_up do |library|
|
22
|
+
library.module_repo PuppetLibrary::ModuleRepo::Proxy.new("http://forge.puppetlabs.com")
|
23
|
+
library.module_repo PuppetLibrary::ModuleRepo::Directory.new("/var/lib/modules")
|
24
|
+
end
|
25
|
+
|
26
|
+
run server
|
data/lib/puppet_library.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# Puppet Library
|
3
|
-
# Copyright (C)
|
3
|
+
# Copyright (C) 2014 drrb
|
4
4
|
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
6
|
# it under the terms of the GNU General Public License as published by
|
@@ -19,7 +19,11 @@ require 'puppet_library/version'
|
|
19
19
|
|
20
20
|
require 'puppet_library/puppet_library'
|
21
21
|
require 'puppet_library/server'
|
22
|
+
require 'puppet_library/http/cache'
|
23
|
+
require 'puppet_library/http/http_client'
|
24
|
+
require 'puppet_library/http/url'
|
22
25
|
require 'puppet_library/module_metadata'
|
23
26
|
require 'puppet_library/module_repo/directory'
|
24
27
|
require 'puppet_library/module_repo/multi'
|
28
|
+
require 'puppet_library/module_repo/proxy'
|
25
29
|
require 'puppet_library/util'
|
@@ -0,0 +1,100 @@
|
|
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
|
+
class Array
|
19
|
+
def deep_merge
|
20
|
+
inject({}) do |merged, map|
|
21
|
+
merged.deep_merge(map)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Hash
|
27
|
+
def deep_merge(other)
|
28
|
+
merge(other) do |key, old_val, new_val|
|
29
|
+
if old_val.instance_of? Array
|
30
|
+
old_val + new_val
|
31
|
+
else
|
32
|
+
new_val
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module PuppetLibrary
|
39
|
+
class ModuleNotFound < Exception
|
40
|
+
end
|
41
|
+
|
42
|
+
class Forge
|
43
|
+
class Config
|
44
|
+
def initialize(module_repo)
|
45
|
+
@module_repo = module_repo
|
46
|
+
end
|
47
|
+
|
48
|
+
def module_repo(repo)
|
49
|
+
@module_repo.add_repo repo
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.configure(&config_block)
|
54
|
+
module_repo = ModuleRepo::Multi.new
|
55
|
+
yield(Config.new(module_repo))
|
56
|
+
Forge.new(module_repo)
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(module_repo)
|
60
|
+
@repo = module_repo
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_module_metadata(author, name)
|
64
|
+
modules = get_metadata(author, name)
|
65
|
+
|
66
|
+
raise ModuleNotFound if modules.empty?
|
67
|
+
|
68
|
+
module_infos = modules.map { |m| m.to_info }
|
69
|
+
module_infos.deep_merge
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_module_metadata_with_dependencies(author, name)
|
73
|
+
full_name = "#{author}/#{name}"
|
74
|
+
|
75
|
+
collect_dependencies_versions(full_name).tap do |versions|
|
76
|
+
raise ModuleNotFound if versions[full_name].empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def collect_dependencies_versions(module_full_name, metadata = {})
|
81
|
+
author, module_name = module_full_name.split "/"
|
82
|
+
module_versions = get_metadata(author, module_name)
|
83
|
+
metadata[module_full_name] = module_versions.map {|v| v.to_version }
|
84
|
+
|
85
|
+
dependencies = module_versions.map {|v| v.dependency_names }.flatten
|
86
|
+
dependencies.each do |dependency|
|
87
|
+
collect_dependencies_versions(dependency, metadata) unless metadata.include? dependency
|
88
|
+
end
|
89
|
+
return metadata
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_module_buffer(author, name, version)
|
93
|
+
@repo.get_module(author, name, version) or raise ModuleNotFound
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_metadata(author, module_name)
|
97
|
+
@repo.get_metadata(author, module_name).map {|metadata| ModuleMetadata.new(metadata)}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,61 @@
|
|
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
|
+
module PuppetLibrary::Http
|
19
|
+
class Cache
|
20
|
+
ARBITRARY_CACHE_TTL_MILLIS = 10 * 1000
|
21
|
+
def initialize(millis_to_live = ARBITRARY_CACHE_TTL_MILLIS)
|
22
|
+
@reaper = Reaper.new(millis_to_live)
|
23
|
+
@cache = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(key)
|
27
|
+
entry = @cache[key]
|
28
|
+
if entry
|
29
|
+
return entry.value unless @reaper.wants_to_kill? entry
|
30
|
+
end
|
31
|
+
|
32
|
+
value = yield
|
33
|
+
@cache[key] = Entry.new(value)
|
34
|
+
return value
|
35
|
+
end
|
36
|
+
|
37
|
+
class Entry
|
38
|
+
attr_accessor :value
|
39
|
+
|
40
|
+
def initialize(value)
|
41
|
+
@birth = Time.now
|
42
|
+
@value = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def age_millis
|
46
|
+
age_seconds = Time.now - @birth
|
47
|
+
age_seconds * 1000
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Reaper
|
52
|
+
def initialize(millis_to_live)
|
53
|
+
@millis_to_live = millis_to_live
|
54
|
+
end
|
55
|
+
|
56
|
+
def wants_to_kill?(entry)
|
57
|
+
entry.age_millis > @millis_to_live
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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 'open-uri'
|
19
|
+
|
20
|
+
module PuppetLibrary::Http
|
21
|
+
class HttpClient
|
22
|
+
def get(url)
|
23
|
+
open_uri(url).read
|
24
|
+
end
|
25
|
+
|
26
|
+
def download(url)
|
27
|
+
open_uri(url)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def open_uri(url)
|
32
|
+
open(url)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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
|
+
module PuppetLibrary::Http
|
19
|
+
class Url
|
20
|
+
def self.normalize(uri_string)
|
21
|
+
unless uri_string =~ /^https?/
|
22
|
+
uri_string = "http://#{uri_string}"
|
23
|
+
end
|
24
|
+
uri_string.sub /\/$/, ""
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# Puppet Library
|
3
|
-
# Copyright (C)
|
3
|
+
# Copyright (C) 2014 drrb
|
4
4
|
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
6
|
# it under the terms of the GNU General Public License as published by
|
@@ -17,7 +17,6 @@
|
|
17
17
|
|
18
18
|
module PuppetLibrary
|
19
19
|
class ModuleMetadata
|
20
|
-
|
21
20
|
def initialize(metadata)
|
22
21
|
@metadata = metadata
|
23
22
|
end
|
@@ -42,6 +41,10 @@ module PuppetLibrary
|
|
42
41
|
@metadata["dependencies"]
|
43
42
|
end
|
44
43
|
|
44
|
+
def description
|
45
|
+
@metadata["description"]
|
46
|
+
end
|
47
|
+
|
45
48
|
def dependency_names
|
46
49
|
dependencies.map {|d| d["name"]}
|
47
50
|
end
|
@@ -51,7 +54,7 @@ module PuppetLibrary
|
|
51
54
|
"author" => author,
|
52
55
|
"full_name" => full_name,
|
53
56
|
"name" => name,
|
54
|
-
"desc" =>
|
57
|
+
"desc" => description,
|
55
58
|
"releases" => [ { "version" => version } ]
|
56
59
|
}
|
57
60
|
end
|
@@ -60,7 +63,9 @@ module PuppetLibrary
|
|
60
63
|
{
|
61
64
|
"file" => "/modules/#{author}-#{name}-#{version}.tar.gz",
|
62
65
|
"version" => version,
|
63
|
-
|
66
|
+
"dependencies" => dependencies.map do |dependency|
|
67
|
+
[ dependency["name"], dependency["version_requirement"] ]
|
68
|
+
end
|
64
69
|
}
|
65
70
|
end
|
66
71
|
end
|