community-zero 1.0.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.
- data/LICENSE +202 -0
- data/README.md +109 -0
- data/Rakefile +18 -0
- data/bin/community-zero +54 -0
- data/lib/community_zero.rb +31 -0
- data/lib/community_zero/chef.rb +20 -0
- data/lib/community_zero/chef/metadata.rb +49 -0
- data/lib/community_zero/endpoint.rb +112 -0
- data/lib/community_zero/endpoints/cookbook_endpoint.rb +68 -0
- data/lib/community_zero/endpoints/cookbook_versions_version_endpoint.rb +59 -0
- data/lib/community_zero/endpoints/cookbooks_endpoint.rb +102 -0
- data/lib/community_zero/endpoints/not_found_endpoint.rb +37 -0
- data/lib/community_zero/endpoints/search_endpoint.rb +44 -0
- data/lib/community_zero/error.rb +25 -0
- data/lib/community_zero/errors/rest_error.rb +30 -0
- data/lib/community_zero/object.rb +25 -0
- data/lib/community_zero/objects/cookbook.rb +99 -0
- data/lib/community_zero/request.rb +60 -0
- data/lib/community_zero/router.rb +55 -0
- data/lib/community_zero/server.rb +124 -0
- data/lib/community_zero/store.rb +149 -0
- data/lib/community_zero/version.rb +20 -0
- metadata +150 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The base class for any endpoint.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class Endpoint
|
23
|
+
require 'community_zero/endpoints/cookbook_endpoint'
|
24
|
+
require 'community_zero/endpoints/cookbook_versions_version_endpoint'
|
25
|
+
require 'community_zero/endpoints/cookbooks_endpoint'
|
26
|
+
require 'community_zero/endpoints/not_found_endpoint'
|
27
|
+
require 'community_zero/endpoints/search_endpoint'
|
28
|
+
|
29
|
+
METHODS = [:get, :put, :post, :delete].freeze
|
30
|
+
|
31
|
+
attr_reader :server
|
32
|
+
|
33
|
+
# Create a new endpoint.
|
34
|
+
#
|
35
|
+
# @param [CommunityZero::Server] server
|
36
|
+
# the server to respond to this endpoint
|
37
|
+
def initialize(server)
|
38
|
+
@server = server
|
39
|
+
end
|
40
|
+
|
41
|
+
# Generate the URL for the given cookbook.
|
42
|
+
#
|
43
|
+
# @param [CommunityZero::Cookbook] cookbook
|
44
|
+
# the coookbook to generate the URL for
|
45
|
+
#
|
46
|
+
# @return [String]
|
47
|
+
# the URL
|
48
|
+
def url_for(cookbook)
|
49
|
+
"#{server.url}/cookbooks/#{cookbook.name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Generate the version URL for the given cookbook and version.
|
53
|
+
#
|
54
|
+
# @param [CommunityZero::Cookbook] cookbook
|
55
|
+
# the coookbook to generate the URL for
|
56
|
+
# @param [String] version
|
57
|
+
# the version to generate a string for
|
58
|
+
#
|
59
|
+
# @return [String]
|
60
|
+
# the URL
|
61
|
+
def version_url_for(cookbook, version)
|
62
|
+
"#{server.url}/cookbooks/#{cookbook.name}/versions/#{version.gsub('.', '_')}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Call the request.
|
66
|
+
#
|
67
|
+
# @param [CommunityZero::Request] request
|
68
|
+
# the request object
|
69
|
+
def call(request)
|
70
|
+
m = request.method.downcase.to_sym
|
71
|
+
|
72
|
+
# Only respond to listed methods
|
73
|
+
unless respond_to?(m)
|
74
|
+
allowed = METHODS.select { |m| respond_to?(m) }.map(&:upcase).join(', ')
|
75
|
+
return [
|
76
|
+
405,
|
77
|
+
{ 'Content-Type' => 'text/plain', 'Allow' => allowed },
|
78
|
+
"Method not allowed: '#{request.env['REQUEST_METHOD']}'"
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
# Must accept JSON
|
83
|
+
unless request.env['HTTP_ACCEPT'].to_s.split(';').include?('application/json')
|
84
|
+
return [
|
85
|
+
406,
|
86
|
+
{ 'Content-Type' => 'text/plain' },
|
87
|
+
'Must accept application/json'
|
88
|
+
]
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
send(m, request)
|
93
|
+
rescue RestError => e
|
94
|
+
error(e.response_code, e.error)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def error(response_code, error)
|
100
|
+
respond(response_code, { 'error' => error })
|
101
|
+
end
|
102
|
+
|
103
|
+
def respond(response_code = 200, content)
|
104
|
+
[
|
105
|
+
response_code,
|
106
|
+
{ 'Content-Type' => 'application/json' },
|
107
|
+
JSON.pretty_generate(content)
|
108
|
+
]
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The endpoint for interacting with a single cookbook.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class CookbookEndpoint < Endpoint
|
23
|
+
# GET /cookbooks/:name
|
24
|
+
def get(request)
|
25
|
+
name = request.path.last
|
26
|
+
cookbook = Store.find(name)
|
27
|
+
|
28
|
+
if cookbook = Store.find(name)
|
29
|
+
respond({
|
30
|
+
'name' => cookbook.name,
|
31
|
+
'maintainer' => cookbook.maintainer,
|
32
|
+
'category' => cookbook.category,
|
33
|
+
'external_url' => cookbook.external_url,
|
34
|
+
'description' => cookbook.description,
|
35
|
+
'average_rating' => cookbook.average_rating,
|
36
|
+
'versions' => cookbook.versions.map { |i| version_url_for(cookbook, i) },
|
37
|
+
'latest_version' => version_url_for(cookbook, cookbook.latest_version),
|
38
|
+
'created_at' => cookbook.created_at,
|
39
|
+
'updated_at' => cookbook.upadated_at,
|
40
|
+
})
|
41
|
+
else
|
42
|
+
respond(404,
|
43
|
+
{
|
44
|
+
'error_code' => 'NOT_FOUND',
|
45
|
+
'error_messages' => ['Resource not found'],
|
46
|
+
}
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# DELETE /cookbooks/:name
|
52
|
+
def delete(request)
|
53
|
+
name = request.path.last
|
54
|
+
|
55
|
+
if cookbook = Store.find(name)
|
56
|
+
cookbook.destroy
|
57
|
+
respond({})
|
58
|
+
else
|
59
|
+
respond(404,
|
60
|
+
{
|
61
|
+
'error_code' => 'NOT_FOUND',
|
62
|
+
'error_messages' => ['Resource not found'],
|
63
|
+
}
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The endpoint for interacting with a single cookbook version.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class CookbookVersionsVersionEndpoint < Endpoint
|
23
|
+
def get(request)
|
24
|
+
name, version = request.path[1], request.path[-1].gsub('_', '.')
|
25
|
+
|
26
|
+
unless cookbook = Store.find(name)
|
27
|
+
return respond(404,
|
28
|
+
{
|
29
|
+
'error_code' => 'NOT_FOUND',
|
30
|
+
'error_messages' => ['Resource not found'],
|
31
|
+
}
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
version = cookbook.latest_version if version == 'latest'
|
36
|
+
cookbook = Store.find(name, version)
|
37
|
+
respond(response_hash_for(cookbook))
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
# The response hash for this cookbook.
|
42
|
+
#
|
43
|
+
# @param [CommunityZero::Cookbook] cookbook
|
44
|
+
# the cookbook to generate a hash for
|
45
|
+
def response_hash_for(cookbook)
|
46
|
+
{
|
47
|
+
'cookbook' => url_for(cookbook),
|
48
|
+
'average_rating' => cookbook.average_rating,
|
49
|
+
'version' => cookbook.version,
|
50
|
+
'license' => cookbook.license,
|
51
|
+
'file' => "http://s3.amazonaws.com/#{cookbook.name}.tgz",
|
52
|
+
'tarball_file_size' => cookbook.name.split('').map(&:ord).inject(&:+) * 25, # don't even
|
53
|
+
'created_at' => cookbook.created_at,
|
54
|
+
'updated_at' => cookbook.upadated_at,
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The endpoint for all cookbooks.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class CookbooksEndpoint < Endpoint
|
23
|
+
require 'rubygems/package'
|
24
|
+
require 'zlib'
|
25
|
+
|
26
|
+
# GET /cookbooks
|
27
|
+
def get(request)
|
28
|
+
start = Integer(request.query_params['start'] || 0)
|
29
|
+
items = Integer(request.query_params['items'] || 10)
|
30
|
+
cookbooks = Store.cookbooks[start...items] || []
|
31
|
+
|
32
|
+
respond({
|
33
|
+
'items' => cookbooks.collect { |cookbook|
|
34
|
+
{
|
35
|
+
'cookbook_name' => cookbook.name,
|
36
|
+
'cookbook_description' => cookbook.description,
|
37
|
+
'cookbook' => url_for(cookbook),
|
38
|
+
'cookbook_maintainer' => cookbook.maintainer
|
39
|
+
}
|
40
|
+
},
|
41
|
+
'total' => Store.size,
|
42
|
+
'start' => start.to_i,
|
43
|
+
})
|
44
|
+
end
|
45
|
+
|
46
|
+
# POST /cookbooks
|
47
|
+
def post(request)
|
48
|
+
params = Rack::Utils::Multipart.parse_multipart(request.env)
|
49
|
+
cookbook = params['cookbook']
|
50
|
+
tarball = params['tarball']
|
51
|
+
|
52
|
+
metadata = Metadata.new(read_tarball(tarball))
|
53
|
+
|
54
|
+
if Store.find(metadata.name, metadata.version)
|
55
|
+
respond(401,
|
56
|
+
{
|
57
|
+
'error_code' => 'ALREADY_EXISTS',
|
58
|
+
'error_messages' => ['Resource already exists'],
|
59
|
+
}
|
60
|
+
)
|
61
|
+
else
|
62
|
+
respond(create_cookbook(metadata).to_hash)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
# Create the cookbook from the metadata.
|
68
|
+
#
|
69
|
+
# @param [CommunityZero::Metadata] metadata
|
70
|
+
# the metadata to create the cookbook from
|
71
|
+
def create_cookbook(metadata)
|
72
|
+
Cookbook.create(
|
73
|
+
:name => metadata.name,
|
74
|
+
:category => nil,
|
75
|
+
:maintainer => metadata.maintainer,
|
76
|
+
:description => metadata.description,
|
77
|
+
:version => metadata.version
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Parse the metadata from the tarball.
|
82
|
+
#
|
83
|
+
# @param [Tempfile] tarball
|
84
|
+
# the temporarily uploaded file
|
85
|
+
def read_tarball(tarball)
|
86
|
+
gzip = Zlib::GzipReader.new(tarball[:tempfile])
|
87
|
+
tar = Gem::Package::TarReader.new(gzip)
|
88
|
+
metadata = nil
|
89
|
+
|
90
|
+
tar.each do |entry|
|
91
|
+
if entry.full_name =~ /metadata\.rb$/
|
92
|
+
metadata = entry.read
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
tar.close
|
97
|
+
|
98
|
+
return metadata
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The general 404 endpoint.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class NotFoundEndpoint < Endpoint
|
23
|
+
def call(request)
|
24
|
+
error("Object not found: #{request.env['REQUEST_PATH']}")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def error(message)
|
29
|
+
[
|
30
|
+
404,
|
31
|
+
{ 'Content-Type' => 'application/json' },
|
32
|
+
JSON.pretty_generate({ 'error' => message })
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The endpoint for searching for cookbooks.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class SearchEndpoint < Endpoint
|
23
|
+
# GET /search?q=QUERY
|
24
|
+
def get(request)
|
25
|
+
q = request.query_params['q'].to_s
|
26
|
+
start = Integer(request.query_params['start'] || 0)
|
27
|
+
items = Integer(request.query_params['items'] || 10)
|
28
|
+
cookbooks = Store.search(q)[start...items] || []
|
29
|
+
|
30
|
+
respond({
|
31
|
+
'items' => cookbooks.collect { |cookbook|
|
32
|
+
{
|
33
|
+
'cookbook_name' => cookbook.name,
|
34
|
+
'cookbook_description' => cookbook.description,
|
35
|
+
'cookbook' => url_for(cookbook),
|
36
|
+
'cookbook_maintainer' => cookbook.maintainer
|
37
|
+
}
|
38
|
+
},
|
39
|
+
'total' => cookbooks.size,
|
40
|
+
'start' => start,
|
41
|
+
})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013, Seth Vargo <sethvargo@gmail.com>
|
3
|
+
# Copyright 2013, Opscode, Inc.
|
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 CommunityZero
|
19
|
+
# The base class for errors.
|
20
|
+
#
|
21
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
22
|
+
class Error < StandardError; end
|
23
|
+
|
24
|
+
require 'community_zero/errors/rest_error'
|
25
|
+
end
|