puppet-library 0.0.2 → 0.1.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.
@@ -0,0 +1,87 @@
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 'puppet_library/http/http_client'
19
+ require 'puppet_library/http/cache'
20
+ require 'puppet_library/http/url'
21
+
22
+ module PuppetLibrary::ModuleRepo
23
+ class Proxy
24
+ def initialize(url, http_client = PuppetLibrary::Http::HttpClient.new, query_cache = PuppetLibrary::Http::Cache.new)
25
+ @url = PuppetLibrary::Http::Url.normalize(url)
26
+ @http_client = http_client
27
+ @query_cache = query_cache
28
+ end
29
+
30
+ def get_module(author, name, version)
31
+ version_info = get_module_version(author, name, version)
32
+ if version_info.nil?
33
+ nil
34
+ else
35
+ download_file(version_info["file"])
36
+ end
37
+ end
38
+
39
+ def get_metadata(author, name)
40
+ module_versions = get_module_versions(author, name)
41
+ module_versions.map do |version_info|
42
+ {
43
+ "name" => "#{author}-#{name}",
44
+ "author" => author,
45
+ "version" => version_info["version"],
46
+ "dependencies" => version_info["dependencies"].map do |(dep_name, dep_spec)|
47
+ { "name" => dep_name, "version_requirement" => dep_spec }
48
+ end
49
+ }
50
+ end
51
+ rescue OpenURI::HTTPError => http_error
52
+ return []
53
+ end
54
+
55
+ private
56
+ def get_module_version(author, name, version)
57
+ module_versions = get_module_versions(author, name)
58
+ module_versions.find do |version_info|
59
+ version_info["version"] == version
60
+ end
61
+ end
62
+
63
+ def get_module_versions(author, name)
64
+ versions = look_up_releases(author, name)
65
+ versions["#{author}/#{name}"]
66
+ end
67
+
68
+ def look_up_releases(author, name)
69
+ response = get("/api/v1/releases.json?module=#{author}/#{name}")
70
+ JSON.parse(response)
71
+ end
72
+
73
+ def download_file(file)
74
+ @http_client.download(url(file))
75
+ end
76
+
77
+ def get(relative_url)
78
+ @query_cache.get(relative_url) do
79
+ @http_client.get(url(relative_url))
80
+ end
81
+ end
82
+
83
+ def url(relative_url)
84
+ @url + relative_url
85
+ end
86
+ end
87
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # Puppet Library
3
- # Copyright (C) 2013 drrb
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
@@ -16,6 +16,7 @@
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  require 'rack'
19
+ require 'puppet_library/forge'
19
20
  require 'puppet_library/module_repo/directory'
20
21
  require 'puppet_library/module_repo/multi'
21
22
 
@@ -41,19 +42,24 @@ module PuppetLibrary
41
42
  option_parser = OptionParser.new do |opts|
42
43
  opts.banner = "Usage: #{File.basename $0} [options]"
43
44
 
44
- options[:port] = "9292"
45
- opts.on("-p", "--port [PORT]", "Port to listen on (defaults to #{options[:port]})") do |port|
45
+ opts.on("-p", "--port [PORT]", "Port to listen on (defaults to whatever Rack wants to use)") do |port|
46
46
  options[:port] = port
47
47
  end
48
48
 
49
- options[:hostname] = "0.0.0.0"
50
- opts.on("-b", "--bind-host [HOSTNAME]", "Host name to bind to (defaults to #{options[:hostname]})") do |hostname|
49
+ opts.on("-s", "--server [SERVER]", "Server to use (defaults to whatever Rack wants to use)") do |server|
50
+ options[:server] = server
51
+ end
52
+
53
+ opts.on("-b", "--bind-host [HOSTNAME]", "Host name to bind to (defaults to whatever Rack wants to use)") do |hostname|
51
54
  options[:hostname] = hostname
52
55
  end
53
56
 
54
- options[:module_dirs] = []
57
+ options[:repositories] = []
55
58
  opts.on("-m", "--module-dir [DIR]", "Directory containing the modules (can be specified multiple times. Defaults to './modules')") do |module_dir|
56
- options[:module_dirs] << module_dir
59
+ options[:repositories] << [ModuleRepo::Directory, module_dir]
60
+ end
61
+ opts.on("-x", "--proxy [URL]", "Remote forge to proxy (can be specified multiple times)") do |url|
62
+ options[:repositories] << [ModuleRepo::Proxy, url]
57
63
  end
58
64
  end
59
65
  begin
@@ -62,29 +68,32 @@ module PuppetLibrary
62
68
  raise ExpectedError, parse_error.message + "\n" + option_parser.help
63
69
  end
64
70
 
65
- if options[:module_dirs].empty?
66
- options[:module_dirs] << "./modules"
67
- end
68
-
69
71
  return options
70
72
  end
71
73
 
72
74
  def build_server(options)
73
- module_repo = ModuleRepo::Multi.new
74
- options[:module_dirs].each do |dir|
75
- subrepo = ModuleRepo::Directory.new(dir)
76
- module_repo.add_repo(subrepo)
75
+ if options[:repositories].empty?
76
+ options[:repositories] << [ ModuleRepo::Proxy, "http://forge.puppetlabs.com" ]
77
+ end
78
+
79
+ Server.set_up do |server|
80
+ options[:repositories].each do |(repo_type, config)|
81
+ subrepo = repo_type.new(config)
82
+ server.module_repo subrepo
83
+ end
77
84
  end
78
- Server.new(module_repo)
79
85
  end
80
86
 
81
87
  def announce_server_start(options)
88
+ options = options.clone
89
+ options.default = "default"
82
90
  @log.puts "Starting Puppet Library server:"
83
91
  @log.puts " |- Port: #{options[:port]}"
84
92
  @log.puts " |- Host: #{options[:hostname]}"
85
- @log.puts " `- Module dirs:"
86
- options[:module_dirs].each do |dir|
87
- @log.puts " - #{dir}"
93
+ @log.puts " |- Server: #{options[:server]}"
94
+ @log.puts " `- Repositories:"
95
+ options[:repositories].each do |(repo_type, config)|
96
+ @log.puts " - #{repo_type}: #{config}"
88
97
  end
89
98
  end
90
99
 
@@ -92,7 +101,8 @@ module PuppetLibrary
92
101
  Rack::Server.start(
93
102
  :app => server,
94
103
  :Host => options[:hostname],
95
- :Port => options[:port]
104
+ :Port => options[:port],
105
+ :server => options[:server]
96
106
  )
97
107
  end
98
108
  end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # Puppet Library
3
- # Copyright (C) 2013 drrb
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,35 +17,19 @@
17
17
 
18
18
  require 'sinatra/base'
19
19
 
20
+ require 'puppet_library/forge'
20
21
  require 'puppet_library/module_metadata'
21
22
  require 'puppet_library/module_repo/multi'
22
23
 
23
- class Array
24
- def deep_merge
25
- inject({}) do |merged, map|
26
- merged.deep_merge(map)
27
- end
28
- end
29
- end
30
-
31
- class Hash
32
- def deep_merge(other)
33
- merge(other) do |key, old_val, new_val|
34
- if old_val.instance_of? Array
35
- old_val + new_val
36
- else
37
- new_val
38
- end
39
- end
40
- end
41
- end
42
-
43
24
  module PuppetLibrary
44
25
  class Server < Sinatra::Base
26
+ def self.set_up(&config_block)
27
+ Server.new(Forge.configure(&config_block))
28
+ end
45
29
 
46
- def initialize(module_repo = ModuleRepo::Multi.new)
30
+ def initialize(forge)
47
31
  super(nil)
48
- @repo = settings.respond_to?(:module_repo) ? settings.module_repo : module_repo
32
+ @forge = forge
49
33
  end
50
34
 
51
35
  configure do
@@ -57,7 +41,7 @@ module PuppetLibrary
57
41
  module_name = params[:module]
58
42
 
59
43
  begin
60
- get_module_metadata(author, module_name).to_json
44
+ @forge.get_module_metadata(author, module_name).to_json
61
45
  rescue ModuleNotFound
62
46
  status 410
63
47
  {"error" => "Could not find module \"#{module_name}\""}.to_json
@@ -67,7 +51,7 @@ module PuppetLibrary
67
51
  get "/api/v1/releases.json" do
68
52
  author, module_name = params[:module].split "/"
69
53
  begin
70
- get_module_metadata_with_dependencies(author, module_name).to_json
54
+ @forge.get_module_metadata_with_dependencies(author, module_name).to_json
71
55
  rescue ModuleNotFound
72
56
  status 410
73
57
  {"error" => "Module #{author}/#{module_name} not found"}.to_json
@@ -82,53 +66,12 @@ module PuppetLibrary
82
66
  content_type "application/octet-stream"
83
67
 
84
68
  begin
85
- get_module_buffer(author, name, version).tap do
69
+ @forge.get_module_buffer(author, name, version).tap do
86
70
  attachment "#{author}-#{name}-#{version}.tar.gz"
87
71
  end
88
72
  rescue ModuleNotFound
89
73
  status 404
90
74
  end
91
75
  end
92
-
93
- private
94
- def get_module_metadata(author, name)
95
- modules = get_metadata(author, name)
96
- raise ModuleNotFound if modules.empty?
97
-
98
- module_infos = modules.map { |m| m.to_info }
99
- module_infos.deep_merge
100
- end
101
-
102
- def get_module_metadata_with_dependencies(author, name)
103
- full_name = "#{author}/#{name}"
104
-
105
- module_queue = []
106
- modules_versions = {}
107
- module_queue << full_name
108
- until module_queue.empty?
109
- module_full_name = module_queue.shift
110
- already_added = modules_versions.include? module_full_name
111
- unless already_added
112
- author, module_name = module_full_name.split "/"
113
- module_versions = get_metadata(author, module_name)
114
- dependencies = module_versions.map {|v| v.dependency_names }.flatten
115
- module_queue += dependencies
116
- modules_versions[module_full_name] = module_versions.map { |v| v.to_version }
117
- end
118
- end
119
- raise ModuleNotFound if modules_versions.values == [[]]
120
- modules_versions
121
- end
122
-
123
- def get_module_buffer(author, name, version)
124
- @repo.get_module(author, name, version) or raise ModuleNotFound
125
- end
126
-
127
- def get_metadata(author, module_name)
128
- @repo.get_metadata(author, module_name).map {|metadata| ModuleMetadata.new(metadata)}
129
- end
130
76
  end
131
77
  end
132
-
133
- class ModuleNotFound < Exception
134
- end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # Puppet Library
3
- # Copyright (C) 2013 drrb
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
@@ -23,7 +23,7 @@ class Array
23
23
  attribute = yield(element)
24
24
  is_duplicate = attr_to_element.include? attribute
25
25
  unless is_duplicate
26
- attr_to_element[yield(element)] = element
26
+ attr_to_element[attribute] = element
27
27
  end
28
28
  !is_duplicate
29
29
  end
@@ -16,5 +16,5 @@
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  module PuppetLibrary
19
- VERSION = "0.0.2"
19
+ VERSION = "0.1.0"
20
20
  end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # Puppet Library
3
- # Copyright (C) 2013 drrb
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
@@ -41,8 +41,10 @@ Gem::Specification.new do |spec|
41
41
 
42
42
  spec.add_development_dependency "bundler", "~> 1.3"
43
43
  spec.add_development_dependency "coveralls"
44
+ spec.add_development_dependency "librarian-puppet"
44
45
  spec.add_development_dependency "mime-types", "< 2"
45
46
  spec.add_development_dependency "pry"
47
+ spec.add_development_dependency "puppet"
46
48
  spec.add_development_dependency "rack-test"
47
49
  spec.add_development_dependency "rake"
48
50
  spec.add_development_dependency "rspec"
@@ -0,0 +1,147 @@
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 'spec_helper'
19
+
20
+ module PuppetLibrary
21
+ describe Forge do
22
+ let(:forge) { Forge.new(module_repo) }
23
+ let(:module_repo) { double('module_repo') }
24
+
25
+ describe "#get_module_buffer" do
26
+ context "module version not found" do
27
+ it "raises an error" do
28
+ expect(module_repo).to receive(:get_module).with("puppetlabs", "apache", "1.0.0").and_return(nil)
29
+
30
+ expect {
31
+ forge.get_module_buffer("puppetlabs", "apache", "1.0.0")
32
+ }.to raise_error ModuleNotFound
33
+ end
34
+ end
35
+
36
+ context "when the module is found" do
37
+ it "returns a buffer containing the module archive" do
38
+ file_buffer = "module content"
39
+ expect(module_repo).to receive(:get_module).with("puppetlabs", "apache", "1.0.0").and_return(file_buffer)
40
+
41
+ result = forge.get_module_buffer("puppetlabs", "apache", "1.0.0")
42
+ expect(result).to eq file_buffer
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "#get_module_metadata" do
48
+ context "when no modules found" do
49
+ it "raises an error" do
50
+ expect(module_repo).to receive(:get_metadata).with("nonexistant", "nonexistant").and_return([])
51
+
52
+ expect {
53
+ forge.get_module_metadata("nonexistant", "nonexistant")
54
+ }.to raise_error ModuleNotFound
55
+ end
56
+ end
57
+
58
+ context "when module versions found" do
59
+ it "returns metadata for all versions" do
60
+ metadata = [ {
61
+ "author" => "puppetlabs",
62
+ "name" => "puppetlabs-apache",
63
+ "description" => "Apache module",
64
+ "version" => "1.0.0"
65
+ }, {
66
+ "author" => "puppetlabs",
67
+ "name" => "puppetlabs-apache",
68
+ "description" => "Apache module",
69
+ "version" => "1.1.0"
70
+ } ]
71
+ expect(module_repo).to receive(:get_metadata).with("puppetlabs", "apache").and_return(metadata)
72
+
73
+ metadata = forge.get_module_metadata("puppetlabs", "apache")
74
+
75
+ expect(metadata["author"]).to eq "puppetlabs"
76
+ expect(metadata["full_name"]).to eq "puppetlabs/apache"
77
+ expect(metadata["name"]).to eq "apache"
78
+ expect(metadata["desc"]).to eq "Apache module"
79
+ expect(metadata["releases"]).to eq [
80
+ {"version" => "1.0.0"},
81
+ {"version" => "1.1.0"}
82
+ ]
83
+ end
84
+ end
85
+ end
86
+
87
+ describe "#get_module_metadata_with_dependencies" do
88
+ context "when no module versions found" do
89
+ it "raises an error" do
90
+ expect(module_repo).to receive(:get_metadata).with("nonexistant", "nonexistant").and_return([])
91
+
92
+ expect {
93
+ forge.get_module_metadata_with_dependencies("nonexistant", "nonexistant")
94
+ }.to raise_error ModuleNotFound
95
+ end
96
+ end
97
+
98
+ context "when module versions found" do
99
+ it "returns metadata for module and dependencies" do
100
+ apache_metadata = [ {
101
+ "author" => "puppetlabs",
102
+ "name" => "puppetlabs-apache",
103
+ "description" => "Apache module",
104
+ "version" => "1.0.0",
105
+ "dependencies" => [
106
+ { "name" => "puppetlabs/stdlib", "version_requirement" => ">= 2.4.0" },
107
+ { "name" => "puppetlabs/concat", "version_requirement" => ">= 1.0.0" }
108
+ ]
109
+ }, {
110
+ "author" => "puppetlabs",
111
+ "name" => "puppetlabs-apache",
112
+ "description" => "Apache module",
113
+ "version" => "1.1.0",
114
+ "dependencies" => [
115
+ { "name" => "puppetlabs/stdlib", "version_requirement" => ">= 2.4.0" },
116
+ { "name" => "puppetlabs/concat", "version_requirement" => ">= 1.0.0" }
117
+ ]
118
+ } ]
119
+ stdlib_metadata = [ {
120
+ "author" => "puppetlabs",
121
+ "name" => "puppetlabs-stdlib",
122
+ "description" => "Stdlib module",
123
+ "version" => "2.0.0",
124
+ "dependencies" => [ ]
125
+ } ]
126
+ concat_metadata = [ {
127
+ "author" => "puppetlabs",
128
+ "name" => "puppetlabs-concat",
129
+ "description" => "Concat module",
130
+ "version" => "1.0.0",
131
+ "dependencies" => [ ]
132
+ } ]
133
+ expect(module_repo).to receive(:get_metadata).with("puppetlabs", "apache").and_return(apache_metadata)
134
+ expect(module_repo).to receive(:get_metadata).with("puppetlabs", "stdlib").and_return(stdlib_metadata)
135
+ expect(module_repo).to receive(:get_metadata).with("puppetlabs", "concat").and_return(concat_metadata)
136
+
137
+ result = forge.get_module_metadata_with_dependencies("puppetlabs", "apache")
138
+ expect(result.keys.sort).to eq(["puppetlabs/apache", "puppetlabs/concat", "puppetlabs/stdlib"])
139
+ expect(result["puppetlabs/apache"].size).to eq(2)
140
+ expect(result["puppetlabs/apache"][0]["file"]).to eq("/modules/puppetlabs-apache-1.0.0.tar.gz")
141
+ expect(result["puppetlabs/apache"][0]["version"]).to eq("1.0.0")
142
+ expect(result["puppetlabs/apache"][0]["version"]).to eq("1.0.0")
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end