1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
#
# Author:: Stephen Delano (<stephen@opscode.com>)
# Author:: Tim Hinderliter (<tim@opscode.com>)
# Copyright:: Copyright (c) 2010, 2011 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/environment'
require 'chef/cookbook_version_selector'
class Environments < Application
include Merb::CookbookVersionHelper
provides :json
before :authenticate_every
before :is_admin, :only => [ :create, :update, :destroy ]
# GET /environments
def index
environment_list = Chef::Environment.cdb_list(true)
display(environment_list.inject({}) { |res, env| res[env.name] = absolute_url(:environment, env.name); res })
end
# GET /environments/:id
def show
begin
environment = Chef::Environment.cdb_load(params[:id])
rescue Chef::Exceptions::CouchDBNotFound => e
raise NotFound, "Cannot load environment #{params[:id]}"
end
environment.couchdb_rev = nil
display environment
end
# POST /environments
def create
env = params["inflated_object"]
exists = true
begin
Chef::Environment.cdb_load(env.name)
rescue Chef::Exceptions::CouchDBNotFound
exists = false
end
raise Conflict, "Environment already exists" if exists
env.cdb_save
self.status = 201
display({:uri => absolute_url(:environment, env.name)})
end
# PUT /environments/:id
def update
raise MethodNotAllowed if params[:id] == "_default"
begin
env = Chef::Environment.cdb_load(params[:id])
rescue Chef::Exceptions::CouchDBNotFound
raise NotFound, "Cannot load environment #{params[:id]}"
end
env.update_from!(params["inflated_object"])
env.cdb_save
env.couchdb_rev = nil
self.status = 200
display(env)
end
# DELETE /environments/:id
def destroy
raise MethodNotAllowed if params[:id] == "_default"
begin
env = Chef::Environment.cdb_load(params[:id])
rescue Chef::Exceptions::CouchDBNotFound
raise NotFound, "Cannot load environment #{params[:id]}"
end
env.cdb_destroy
display(env)
end
# GET /environments/:environment_id/cookbooks
# returns data in the format of:
# {"apache2" => {
# :url => "http://url",
# :versions => [{:url => "http://url/1.0.0", :version => "1.0.0"}, {:url => "http://url/0.0.1", :version=>"0.0.1"}]
# }
# }
def list_cookbooks
begin
filtered_cookbooks = Chef::Environment.cdb_load_filtered_cookbook_versions(params[:environment_id])
rescue Chef::Exceptions::CouchDBNotFound
raise NotFound, "Cannot load environment #{params[:environment_id]}"
end
num_versions = num_versions!
display(filtered_cookbooks.inject({}) {|res, (cookbook_name,versions)|
versions.map!{|v| v.version.to_s}
res[cookbook_name] = expand_cookbook_urls(cookbook_name, versions, num_versions)
res
})
end
# POST /environment/:environment_id/cookbook_versions
#
# Take the given run_list and return the versions of cookbooks that would
# be used after applying the constraints of the given environment.
#
# INPUT:
# :run_list = an Array of String's, e.g.,
# ["recipe[apache2]", "recipe[runit]"]
#
# OUT:
# Hash of cookbook names to version/url hash, e.g.,
# {
# "apache2": {
# "version": "1.2.3",
# "url" => "http://.../cookbooks/apache2/1.2.3"
# },
# "runit": {
# "version": "2.3.4",
# "url" => "http://.../cookbooks/runit/2.3.4"
# }
# }
#
# NOTE: This method is a POST, not because it's a mutator (it's idempotent),
# but the run_list can likely exceed Merb's query string limit for GET
# of 1024 characters.
def cookbook_versions_for_run_list
begin
# not possible to be nil due to the route to get us to this API
# endpoint
environment_input = params[:environment_id]
run_list_input = params[:run_list]
raise BadRequest, "Missing param: run_list" unless run_list_input
raise BadRequest, "Param run_list is not an Array: #{run_list_input.class}" unless run_list_input.is_a?(Array)
# Convert the input array of strings to a RunList containing
# RunListItem's.
run_list = Chef::RunList.new
run_list_input.each do |run_list_item_string|
run_list << run_list_item_string
end
# Expand the run list in the scope of the specified environment.
names_to_cookbook_version = Chef::CookbookVersionSelector.expand_to_cookbook_versions(run_list, environment_input)
rescue Chef::Exceptions::CouchDBNotFound
raise NotFound, "Cannot load environment #{params[:environment_id]}"
rescue Chef::Exceptions::CookbookVersionConflict => e
error = { :message => e.message,
#:unsatisfiable_solution_constraint => e.unsatisfiable_solution_constraint,
#:non_existent_cookbooks => e.disabled_non_existent_packages,
#:most_constrained_cookbooks => e.disabled_most_constrained_packages
}
raise PreconditionFailed, error.to_json
end
# convert the hash which is
# name => CookbookVersion
# to
# name => {:version => version, :url => url}
names_to_url_and_version = names_to_cookbook_version.values.inject({}) do |res, cb_version|
name = cb_version.name
res[name] = {
:version => cb_version.version,
:url => absolute_url(:cookbook_version, :cookbook_name => name, :cookbook_version => cb_version.version)
}
res
end
display(names_to_url_and_version)
end
# GET /environments/:environment_id/cookbooks/:cookbook_id
# returns data in the format of:
# {"apache2" => {
# :url => "http://url",
# :versions => [{:url => "http://url/1.0.0", :version => "1.0.0"}, {:url => "http://url/0.0.1", :version=>"0.0.1"}]
# }
# }
def cookbook
cookbook_name = params[:cookbook_id]
begin
filtered_cookbooks = Chef::Environment.cdb_load_filtered_cookbook_versions(params[:environment_id])
rescue Chef::Exceptions::CouchDBNotFound
raise NotFound, "Cannot load environment #{params[:environment_id]}"
end
raise NotFound, "Cannot load cookbook #{cookbook_name}" unless filtered_cookbooks.has_key?(cookbook_name)
versions = filtered_cookbooks[cookbook_name].map{|v| v.version.to_s}
num_versions = num_versions!("all")
display({ cookbook_name => expand_cookbook_urls(cookbook_name, versions, num_versions) })
end
# GET /environments/:environment/recipes
def list_recipes
display(Chef::Environment.cdb_load_filtered_recipe_list(params[:environment_id]))
end
# GET /environments/:environment_id/nodes
def list_nodes
node_list = Chef::Node.cdb_list_by_environment(params[:environment_id])
display(node_list.inject({}) {|r,n| r[n] = absolute_url(:node, n); r})
end
# GET /environments/:environment_id/roles/:role_id
def role
begin
role = Chef::Role.cdb_load(params[:role_id])
rescue Chef::Exceptions::CouchDBNotFound
raise NotFound, "Cannot load role #{params[:role_id]}"
end
display("run_list" => role.env_run_lists[params[:environment_id]])
end
private
end
|