puppet-forge-server 1.0.1

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,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'open-uri'
18
+ require 'open_uri_redirections'
19
+
20
+ module PuppetForgeServer::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, 'User-Agent' => "Puppet-Forge-Server/#{PuppetForgeServer::VERSION}", :allow_redirections => :safe)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module PuppetForgeServer::Models
18
+ class Builder
19
+ def initialize(attributes={})
20
+ attributes.each do |name, value|
21
+ send("#{name}=", value) unless value.to_s.empty?
22
+ end
23
+ end
24
+
25
+ def method_missing (method_name, *args, &block)
26
+ STDERR.puts "Method #{method_name} with args #{args} not found in #{self.class.to_s}" unless method_name == :to_ary
27
+ end
28
+
29
+ def to_hash
30
+ hash = {}
31
+ self.instance_variables.each do |var|
32
+ hash[var.to_s.delete('@')] = self.instance_variable_get var
33
+ end
34
+ hash
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ module PuppetForgeServer::Models
19
+ class Metadata < Builder
20
+
21
+ attr_accessor :author, :name, :version, :dependencies, :summary, :description, :project_page, :types
22
+ attr_accessor :checksums, :source, :license, :issues_url, :operatingsystem_support, :requirements
23
+
24
+ def initialize(attributes)
25
+ super(attributes)
26
+ end
27
+
28
+ def ==(other)
29
+ other && self.class.equal?(other.class) &&
30
+ @author == other.author &&
31
+ @name == other.name &&
32
+ @version == other.version
33
+ end
34
+
35
+ def hash
36
+ @author.hash ^ @name.hash ^ @version.hash
37
+ end
38
+
39
+ def eql?(other)
40
+ other && self.class.equal?(other.class) &&
41
+ @author.eql?(other.author) &&
42
+ @name.eql?(other.name) &&
43
+ @version.eql?(other.version)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 drrb
4
+ # Copyright 2014 North Development AB
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'rubygems/package'
19
+
20
+ class Hash
21
+ def deep_merge(other)
22
+ merge(other) do |key, old_val, new_val|
23
+ if old_val.instance_of? Array
24
+ old_val + new_val
25
+ else
26
+ new_val
27
+ end
28
+ end
29
+ end
30
+
31
+ def deep_merge!(other)
32
+ replace(deep_merge(other))
33
+ end
34
+ end
35
+
36
+ class Array
37
+ def deep_merge
38
+ inject({}) do |merged, map|
39
+ merged.deep_merge(map)
40
+ end
41
+ end
42
+
43
+ def version_sort_by
44
+ sort_by do |element|
45
+ version = yield(element)
46
+ Gem::Version.new(version)
47
+ end
48
+ end
49
+ end
50
+
51
+ # Used by PuppetForgeServer::Utils::Archiver
52
+ class Gem::Package::TarReader
53
+ # Old versions of RubyGems don't include Enumerable in here
54
+ def find
55
+ each do |entry|
56
+ if yield(entry)
57
+ return entry
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,88 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'rack/mount'
18
+
19
+ module PuppetForgeServer
20
+ class Server
21
+ include PuppetForgeServer::Utils::OptionParser
22
+ include PuppetForgeServer::Utils::Http
23
+
24
+ def initialize(log = STDERR)
25
+ @log = log
26
+ end
27
+
28
+ def go(args)
29
+ begin
30
+ options = parse_options(args)
31
+ backends = backends(options)
32
+ server = build(backends)
33
+ announce(options, backends)
34
+ start(server, options)
35
+ rescue PuppetForgeServer::Errors::Expected
36
+ @log.puts "Error: #{error}"
37
+ end
38
+ end
39
+
40
+ def build(backends)
41
+ Rack::Mount::RouteSet.new do |set|
42
+ set.add_route PuppetForgeServer::App::Version1.new(backends)
43
+ set.add_route ::PuppetForgeServer::App::Version3.new(backends)
44
+ end
45
+ end
46
+
47
+ def announce(options, backends)
48
+ options = options.clone
49
+ options.default = 'default'
50
+ @log.puts " +- Daemonizing: #{options[:daemonize]}"
51
+ @log.puts " |- Port: #{options[:port]}"
52
+ @log.puts " |- Host: #{options[:hostname]}"
53
+ @log.puts " |- Pidfile: #{options[:pidfile]}" if options[:pidfile]
54
+ @log.puts " |- Server: #{options[:server]}"
55
+ @log.puts ' `- Backends:'
56
+ backends.each do |backend|
57
+ @log.puts " - #{backend.inspect}"
58
+ end
59
+ end
60
+
61
+ def start(server, options)
62
+ FileUtils.mkdir_p File.dirname(options[:pidfile]) if options[:pidfile]
63
+ Rack::Server.start(
64
+ :app => server,
65
+ :Host => options[:hostname],
66
+ :Port => options[:port],
67
+ :server => options[:server],
68
+ :daemonize => options[:daemonize],
69
+ :pid => options[:pidfile]
70
+ )
71
+ end
72
+
73
+ private
74
+ def backends(options)
75
+ options[:backend].map do |type, typed_backends|
76
+ typed_backends.map do |url|
77
+ case type
78
+ when 'Proxy'
79
+ @log.puts "Detecting API version for #{url}..."
80
+ PuppetForgeServer::Backends.const_get("#{type}V#{get_api_version(url)}").new(url.chomp('/'), options[:cache_basedir])
81
+ else
82
+ PuppetForgeServer::Backends.const_get(type).new(url)
83
+ end
84
+ end
85
+ end.flatten.sort_by { |backend| backend.PRIORITY }
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'rubygems/package'
18
+ require 'zlib'
19
+
20
+ module PuppetForgeServer::Utils
21
+ module Archiver
22
+ def read_entry(path, entry_name_regex)
23
+ tar = Gem::Package::TarReader.new(Zlib::GzipReader.open(path))
24
+ tar.rewind
25
+ entry = tar.find { |e| e.full_name =~ entry_name_regex } or raise "Couldn't find entry in archive matching #{entry_name_regex.inspect}"
26
+ entry.read
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ module PuppetForgeServer::Utils
19
+ module Buffer
20
+ def download(buffer)
21
+ headers = buffer.respond_to?(:size) ? {'Content-Length' => buffer.size.to_s} : {}
22
+ [200, headers, buffer]
23
+ end
24
+
25
+ def get_buffer(backends, path)
26
+ backends.each do |backend|
27
+ buffer = backend.get_file_buffer(path)
28
+ return buffer if buffer
29
+ end
30
+ nil
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 North Development AB
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ module PuppetForgeServer::Utils
19
+ module Http
20
+ def get_api_version(url)
21
+ check_url = '/v3/modules/puppetlabs-stdlib'
22
+ begin
23
+ PuppetForgeServer::Http::HttpClient.new.get("#{url.chomp('/')}#{check_url}")
24
+ 3
25
+ rescue OpenURI::HTTPError
26
+ 1
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,76 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 drrb
4
+ # Copyright 2014 North Development AB
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'optparse'
19
+ require 'tmpdir'
20
+
21
+ module PuppetForgeServer::Utils
22
+ module OptionParser
23
+ include PuppetForgeServer::Utils::Url
24
+
25
+ @@DEFAULT_DAEMONIZE = false
26
+ @@DEFAULT_PORT = 8080
27
+ @@DEFAULT_PID_FILE = File.join(Dir.tmpdir.to_s, 'puppet-forge-server', 'server.pid')
28
+ @@DEFAULT_CACHE_DIR = File.join(Dir.tmpdir.to_s, 'puppet-forge-server', 'cache')
29
+
30
+ def parse_options(args)
31
+ options = {:daemonize => @@DEFAULT_DAEMONIZE, :cache_basedir => @@DEFAULT_CACHE_DIR, :port => @@DEFAULT_PORT}
32
+ option_parser = ::OptionParser.new do |opts|
33
+ opts.banner = "Usage: #{File.basename $0} [options]"
34
+ opts.version = PuppetForgeServer::VERSION
35
+
36
+ opts.on('-p', '--port PORT', 'Port to listen on (defaults to whatever Rack wants to use)') do |port|
37
+ options[:port] = port
38
+ end
39
+
40
+ opts.on('-b', '--bind-host HOSTNAME', 'Host name to bind to (defaults to whatever Rack wants to use)') do |hostname|
41
+ options[:hostname] = hostname
42
+ end
43
+
44
+ opts.on('--daemonize', 'Run the server in the background') do
45
+ options[:daemonize] = true
46
+ options[:pidfile] = @@DEFAULT_PID_FILE unless options[:pidfile]
47
+ end
48
+
49
+ opts.on('--pidfile FILE', 'Write a pidfile to this location after starting') do |pidfile|
50
+ options[:pidfile] = pidfile
51
+ end
52
+
53
+ options[:backend] = {'Directory' => [], 'Proxy' => [], 'Source' => []}
54
+ opts.on('-m', '--module-dir DIR', 'Directory containing packaged modules (can be specified multiple times)') do |module_dir|
55
+ options[:backend]['Directory'] << module_dir
56
+ end
57
+ opts.on('-x', '--proxy URL', 'Remote forge to proxy (can be specified multiple times)') do |url|
58
+ options[:backend]['Proxy'] << normalize_url(url)
59
+ end
60
+ opts.on('--source-dir DIR', "Directory containing a module's source (can be specified multiple times)") do |module_dir|
61
+ options[:backend]['Source'] << module_dir
62
+ end
63
+
64
+ opts.on('--cache-basedir DIR', "Cache all proxies' downloaded modules under this directory") do |cache_basedir|
65
+ options[:cache_basedir] = cache_basedir
66
+ end
67
+ end
68
+ begin
69
+ option_parser.parse(args)
70
+ rescue ::OptionParser::InvalidOption => parse_error
71
+ raise PuppetForgeServer::Errors::Expected, parse_error.message + "\n" + option_parser.help
72
+ end
73
+ return options
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 drrb
4
+ # Copyright 2014 North Development AB
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'uri'
19
+
20
+ module PuppetForgeServer::Utils
21
+ module Url
22
+ def normalize_url(uri_string)
23
+ begin
24
+ url = URI.parse(uri_string)
25
+ rescue URI::InvalidURIError => e
26
+ raise PuppetForgeServer::Error::Expected, "Invalid URL '#{uri_string}': #{e.message}"
27
+ end
28
+ if url.scheme
29
+ raise PuppetForgeServer::Error::Expected, "Invalid URL '#{uri_string}': unsupported protocol '#{url.scheme}'" unless url.scheme =~ /^https?$/
30
+ else
31
+ uri_string = "http://#{uri_string}"
32
+ end
33
+ uri_string.sub /\/$/, ''
34
+ end
35
+ end
36
+ end