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.
@@ -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