vmc_knife 0.0.01
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 +24 -0
- data/README.md +14 -0
- data/bin/vmc_knife +6 -0
- data/lib/restclient/restclient_add_timeout.rb +40 -0
- data/lib/vmc_knife/cli_extensions.rb +138 -0
- data/lib/vmc_knife/commands/knife_cmds.rb +186 -0
- data/lib/vmc_knife/json_diff.rb +83 -0
- data/lib/vmc_knife/json_expander.rb +95 -0
- data/lib/vmc_knife/version.rb +8 -0
- data/lib/vmc_knife/vmc_knife.rb +748 -0
- data/lib/vmc_knife.rb +23 -0
- metadata +149 -0
@@ -0,0 +1,748 @@
|
|
1
|
+
require 'vmc/client'
|
2
|
+
require 'json'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module VMC
|
6
|
+
module KNIFE
|
7
|
+
|
8
|
+
# Read/Write the JSON for a recipe.
|
9
|
+
# Does not map the actual JSON into a new ruby object.
|
10
|
+
class Root
|
11
|
+
attr_accessor :wrapped
|
12
|
+
def initialize(data)
|
13
|
+
if data.kind_of? Hash
|
14
|
+
@wrapped = data
|
15
|
+
elsif data.kind_of? String
|
16
|
+
@wrapped = VMC::KNIFE::JSON_EXPANDER.expand_json data
|
17
|
+
elsif data.kind_of? Root
|
18
|
+
@wrapped = data.wrapped
|
19
|
+
else
|
20
|
+
raise "Unexpected data #{data}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
def sub_domain()
|
24
|
+
@wrapped['sub_domain']
|
25
|
+
end
|
26
|
+
def target()
|
27
|
+
@wrapped['target']
|
28
|
+
end
|
29
|
+
def user()
|
30
|
+
@wrapped['user']
|
31
|
+
end
|
32
|
+
def target()
|
33
|
+
@wrapped['target']
|
34
|
+
end
|
35
|
+
def recipes(regexp=nil)
|
36
|
+
regexp||=/.*/
|
37
|
+
res = Array.new
|
38
|
+
@wrapped['recipes'].each do |recipe|
|
39
|
+
res << Recipe.new(self, recipe) if (regexp =~ recipe['name'])
|
40
|
+
end
|
41
|
+
res
|
42
|
+
end
|
43
|
+
def recipe(name)
|
44
|
+
res = @wrapped['recipes'].select {|v| v['name'] == name}
|
45
|
+
Recipe.new self, res.first unless res.empty?
|
46
|
+
end
|
47
|
+
def first_recipe(name)
|
48
|
+
Recipe.new self, @wrapped['recipes'][0] unless @wrapped['recipes'].empty?
|
49
|
+
end
|
50
|
+
def to_json()
|
51
|
+
@wrapped.to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class Recipe
|
57
|
+
attr_accessor :wrapped, :root
|
58
|
+
# root: Root
|
59
|
+
# data: The recipe's data. not the root of the json.
|
60
|
+
def initialize(root,data)
|
61
|
+
@wrapped = data
|
62
|
+
@root = root
|
63
|
+
end
|
64
|
+
#An application.
|
65
|
+
def application(name)
|
66
|
+
Application.new @root, @wrapped['applications'][name], name
|
67
|
+
end
|
68
|
+
def applications(regexp=nil)
|
69
|
+
regexp||=/.*/
|
70
|
+
res = Array.new
|
71
|
+
@wrapped['applications'].each_pair do |name,application|
|
72
|
+
res << Application.new(@root, application, name) if regexp =~ name
|
73
|
+
end
|
74
|
+
res
|
75
|
+
end
|
76
|
+
|
77
|
+
#A dataservice.
|
78
|
+
def data_service(name)
|
79
|
+
DataService.new @root, @wrapped['data_services'][name], name
|
80
|
+
end
|
81
|
+
def data_services(regexp=nil)
|
82
|
+
regexp||=/.*/
|
83
|
+
res = Array.new
|
84
|
+
@wrapped['data_services'].each_pair do |name,service|
|
85
|
+
res << DataService.new(@root, service, name) if regexp =~ name
|
86
|
+
end
|
87
|
+
res
|
88
|
+
|
89
|
+
end
|
90
|
+
def to_json()
|
91
|
+
@wrapped.to_json
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# Read/Write the JSON for a dataservice.
|
97
|
+
# Does not map the actual JSON into a new ruby object.
|
98
|
+
class DataService
|
99
|
+
attr_accessor :wrapped, :role_name, :root
|
100
|
+
def initialize(root, data, role_name)
|
101
|
+
@root = root
|
102
|
+
@wrapped = data
|
103
|
+
@role_name = role_name
|
104
|
+
end
|
105
|
+
# returns the name of the service for cloudfoundry
|
106
|
+
def name()
|
107
|
+
@wrapped['name']
|
108
|
+
end
|
109
|
+
def vendor()
|
110
|
+
@wrapped['vendor']
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns a vcap manifest that can be used
|
114
|
+
# to create a new data-service to vcap's cloud_controller.
|
115
|
+
def to_vcap_manifest()
|
116
|
+
#TODO
|
117
|
+
@wrapped
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# Read/Write the JSON for an application.
|
123
|
+
# Does not map the actual JSON into a new ruby object.
|
124
|
+
class Application
|
125
|
+
attr_accessor :wrapped, :role_name, :root
|
126
|
+
def initialize(root, data, role_name)
|
127
|
+
@root = root
|
128
|
+
@wrapped = data
|
129
|
+
@role_name = role_name
|
130
|
+
end
|
131
|
+
# Returns the application name (different from the application role name.)
|
132
|
+
def name()
|
133
|
+
@wrapped['name']
|
134
|
+
end
|
135
|
+
def uris()
|
136
|
+
@wrapped['uris']
|
137
|
+
end
|
138
|
+
def memory()
|
139
|
+
@wrapped['resources']['memory']
|
140
|
+
end
|
141
|
+
def env()
|
142
|
+
ApplicationEnvironment.new @wrapped['env'], self
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns a vcap manifest that can be used
|
146
|
+
# to push/update an application to vcap's cloud_controller.
|
147
|
+
def to_vcap_manifest()
|
148
|
+
# This is pretty much identical to the json wrapped here except for the environment variables.
|
149
|
+
# if there are differences we will take care of them here.
|
150
|
+
@wrapped
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# Read/Write the application environment. a list of strings
|
156
|
+
# where the first '=' character separate the key and values.
|
157
|
+
class ApplicationEnvironment
|
158
|
+
attr_accessor :wrapped, :application
|
159
|
+
def initialize(data, application)
|
160
|
+
@wrapped = data
|
161
|
+
@application = application
|
162
|
+
end
|
163
|
+
#Sets a variable. Replaces other environment variables with the
|
164
|
+
#same name if there is such a thing.
|
165
|
+
def set(name,value)
|
166
|
+
foundit = false
|
167
|
+
@wrapped.map! do |item|
|
168
|
+
/^([\w]*)=(.*)$/ =~ item
|
169
|
+
#puts "#{name} 1=#{$1} and 2=#{$2}"
|
170
|
+
if ($1 == name)
|
171
|
+
#puts "updating #{$1}"
|
172
|
+
foundit = true
|
173
|
+
"#{$1}=#{value}"
|
174
|
+
else
|
175
|
+
item
|
176
|
+
end
|
177
|
+
end
|
178
|
+
#puts "appending #{name}=#{value}" unless foundit
|
179
|
+
append(name,value) unless foundit
|
180
|
+
end
|
181
|
+
def get(name)
|
182
|
+
@wrapped.each do |e|
|
183
|
+
/^([\w]*)=(.*)$/ =~ e
|
184
|
+
if ($1 == name)
|
185
|
+
#puts "#{k}=name{v}"
|
186
|
+
return $2
|
187
|
+
end
|
188
|
+
end
|
189
|
+
return nil
|
190
|
+
end
|
191
|
+
def del(name)
|
192
|
+
@wrapped.keep_if {|v| v =~ /^#{name}=/; $0.nil?}
|
193
|
+
end
|
194
|
+
def append(name,value)
|
195
|
+
@wrapped << "#{name}=#{value}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class RecipesConfigurationApplier
|
200
|
+
attr_accessor :root, :client, :applications, :recipes, :data_services
|
201
|
+
# Select the applications and data-services to configure according to the values
|
202
|
+
# in the SaaS manifest. When the selector is nil all of them are selected.
|
203
|
+
def initialize(manifest, client, recipe_sel=nil, application_sel=nil, service_sel=nil)
|
204
|
+
@root = Root.new manifest
|
205
|
+
@client = client
|
206
|
+
@recipes = @root.recipes(recipe_sel)
|
207
|
+
@applications = Array.new
|
208
|
+
@data_services = Array.new
|
209
|
+
@recipes.each do |recipe|
|
210
|
+
@applications = @applications + recipe.applications(application_sel)
|
211
|
+
@data_services = @data_services + recipe.data_services(service_sel)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
# Only for testing: inject json
|
215
|
+
def __set_current(current_services=nil,current_services_info=nil)
|
216
|
+
@current_services = current_services
|
217
|
+
@current_services_info = current_services_info
|
218
|
+
end
|
219
|
+
def updates_pending()
|
220
|
+
return @updates_report if @updates_report
|
221
|
+
@current_services ||= @client.services
|
222
|
+
@current_services_info ||= @client.services_info
|
223
|
+
res = Hash.new
|
224
|
+
data_services_updates = Hash.new
|
225
|
+
applications_updates = Hash.new
|
226
|
+
@data_service_updaters = Hash.new
|
227
|
+
@application_updaters = Hash.new
|
228
|
+
@data_services.each do |data_service|
|
229
|
+
unless @data_service_updaters[data_service.name]
|
230
|
+
data_service_updater = DataServiceManifestApplier.new data_service, @client, @current_services, @current_services_info
|
231
|
+
@data_service_updaters[data_service.name] = data_service_updater
|
232
|
+
updates = data_service_updater.updates_pending
|
233
|
+
data_services_updates[data_service.name] = updates if updates
|
234
|
+
end
|
235
|
+
end
|
236
|
+
@applications.each do |application|
|
237
|
+
unless @application_updaters[application.name]
|
238
|
+
application_updater = ApplicationManifestApplier.new application, @client
|
239
|
+
@application_updaters[application.name] = application_updater
|
240
|
+
updates = application_updater.updates_pending
|
241
|
+
applications_updates[application.name] = updates if updates
|
242
|
+
end
|
243
|
+
end
|
244
|
+
res['services'] = data_services_updates unless data_services_updates.empty?
|
245
|
+
res['applications'] = applications_updates unless applications_updates.empty?
|
246
|
+
@updates_report = res
|
247
|
+
puts JSON.pretty_generate @updates_report
|
248
|
+
@updates_report
|
249
|
+
end
|
250
|
+
def upload()
|
251
|
+
@applications.each do |application|
|
252
|
+
application_updater = ApplicationManifestApplier.new application, @client
|
253
|
+
application_updater.upload
|
254
|
+
end
|
255
|
+
end
|
256
|
+
def restart()
|
257
|
+
stop()
|
258
|
+
start()
|
259
|
+
end
|
260
|
+
def stop()
|
261
|
+
@applications.each do |application|
|
262
|
+
application_updater = ApplicationManifestApplier.new application, @client
|
263
|
+
application_updater.stop
|
264
|
+
end
|
265
|
+
end
|
266
|
+
def start()
|
267
|
+
@applications.each do |application|
|
268
|
+
application_updater = ApplicationManifestApplier.new application, @client
|
269
|
+
application_updater.start
|
270
|
+
end
|
271
|
+
end
|
272
|
+
def execute()
|
273
|
+
return if updates_pending.empty?
|
274
|
+
@data_service_updaters.each do |name,data_service_updater|
|
275
|
+
data_service_updater.execute
|
276
|
+
end
|
277
|
+
@application_updaters.each do |name,application_updater|
|
278
|
+
application_updater.execute
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
class DataServiceManifestApplier
|
283
|
+
attr_accessor :data_service_json, :client, :current_services, :current_services_info
|
284
|
+
def initialize(data_service,client,current_services=nil,current_services_info=nil)
|
285
|
+
@client = client
|
286
|
+
if data_service.kind_of? Hash
|
287
|
+
@data_service_json = data_service
|
288
|
+
elsif data_service.kind_of? DataService
|
289
|
+
@data_service_json = data_service.wrapped
|
290
|
+
else
|
291
|
+
raise "Unexpected type of object to describe the data_service #{data_service}"
|
292
|
+
end
|
293
|
+
raise "Can't find the name of the data_service" if @data_service_json['name'].nil?
|
294
|
+
end
|
295
|
+
def current()
|
296
|
+
return @current unless @current.nil?
|
297
|
+
@current_services ||= @client.services
|
298
|
+
@current_services_info ||= @client.services_info
|
299
|
+
@current_services.each do |service|
|
300
|
+
if service[:name] == @data_service_json['name']
|
301
|
+
@current = service
|
302
|
+
break
|
303
|
+
end
|
304
|
+
end
|
305
|
+
@current ||= Hash.new # that would be a new service.
|
306
|
+
end
|
307
|
+
|
308
|
+
# Only for testing: inject json
|
309
|
+
def __set_current(current,current_services=nil,current_services_info=nil)
|
310
|
+
@current = current
|
311
|
+
@current_services = current_services
|
312
|
+
@current_services_info = current_services_info
|
313
|
+
end
|
314
|
+
def updates_pending()
|
315
|
+
vendor = @data_service_json['vendor']
|
316
|
+
name = @data_service_json['name']
|
317
|
+
sh = service_hash()
|
318
|
+
return "Create data-service #{name} vendor #{vendor}" if current().empty?
|
319
|
+
end
|
320
|
+
def execute()
|
321
|
+
return unless updates_pending
|
322
|
+
service_man = service_hash()
|
323
|
+
puts "Calling client.create_service #{@data_service_json['vendor']}, #{@data_service_json['name']}"
|
324
|
+
client.create_service @data_service_json['vendor'], @data_service_json['name']
|
325
|
+
end
|
326
|
+
# Returns the service manifest for the vendor.
|
327
|
+
# If the service vendor ( = type) is not provided by this vcap install
|
328
|
+
# An exception is raised.
|
329
|
+
def service_hash()
|
330
|
+
searched_vendor = @data_service_json['vendor']
|
331
|
+
current()
|
332
|
+
# in the vmc.rb code there is a comment that says 'FIXME!'
|
333
|
+
@current_services_info.each do |service_type, value|
|
334
|
+
value.each do |vendor, version|
|
335
|
+
version.each do |version_str, service_descr|
|
336
|
+
if searched_vendor == service_descr[:vendor]
|
337
|
+
return {
|
338
|
+
"type" => service_descr[:type], "tier" => 'free',
|
339
|
+
"vendor" => searched_vendor, "version" => version_str
|
340
|
+
}
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
raise "vcap does not provide a data-service which vendor is #{searched_vendor}"
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
end
|
350
|
+
class ApplicationManifestApplier
|
351
|
+
attr_accessor :application_json, :client, :current_name
|
352
|
+
# @param application The application object as defined in the SaaS manifest
|
353
|
+
# or the JSON for it.
|
354
|
+
# @param client the vmc client object. assumed that it is logged in.
|
355
|
+
def initialize(application, client, old_name=nil)
|
356
|
+
@client = client
|
357
|
+
if application.kind_of? Hash
|
358
|
+
@application_json = application
|
359
|
+
elsif application.kind_of? Application
|
360
|
+
@application_json = application.wrapped
|
361
|
+
else
|
362
|
+
raise "Unexpected type of object to describe the application #{application}"
|
363
|
+
end
|
364
|
+
raise "Can't find the name of the application" if @application_json['name'].nil?
|
365
|
+
@current_name = old_name
|
366
|
+
@current_name ||= @application_json['old_name']
|
367
|
+
@current_name ||= @application_json['name']
|
368
|
+
end
|
369
|
+
|
370
|
+
def safe_app_info(name)
|
371
|
+
begin
|
372
|
+
return @client.app_info(name)
|
373
|
+
rescue VMC::Client::NotFound
|
374
|
+
#expected
|
375
|
+
return
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def current()
|
380
|
+
return @current unless @current.nil?
|
381
|
+
@current = safe_app_info(@current_name)
|
382
|
+
@current ||= safe_app_info(@application_json['name']) # in case the rename occurred already.
|
383
|
+
@current ||= Hash.new # that would be a new app.
|
384
|
+
end
|
385
|
+
|
386
|
+
# Only for testing: inject json
|
387
|
+
def __set_current(current)
|
388
|
+
@current = current
|
389
|
+
end
|
390
|
+
|
391
|
+
def execute()
|
392
|
+
diff = updates_pending()
|
393
|
+
if diff && diff.size > 0
|
394
|
+
if @current['name'].nil? && @current[:name].nil?
|
395
|
+
puts "Creating #{@application_json['name']} with #{updated_manifest.inspect}"
|
396
|
+
@client.create_app(@application_json['name'], updated_manifest)
|
397
|
+
elsif @current['name'] != @application_json['name']
|
398
|
+
# This works for renaming the application too.
|
399
|
+
puts "Updating #{@application_json['name']} with #{updated_manifest.inspect}"
|
400
|
+
@client.update_app(@application_json['name'], updated_manifest)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def upload()
|
406
|
+
raise "The application #{@application_json['name']} does not exist yet" if current().empty?
|
407
|
+
return unless @application_json['repository']
|
408
|
+
url = @application_json['repository']['url']
|
409
|
+
Dir.chdir(ENV['HOME']) do
|
410
|
+
FileUtils.mkdir_p "vmc_knife_downloads/#{@application_json['name']}"
|
411
|
+
Dir.chdir("vmc_knife_downloads/#{@application_json['name']}") do
|
412
|
+
if Dir.entries(Dir.pwd).size == 2
|
413
|
+
puts "Dir.entries(#{Dir.pwd}).size #{Dir.entries(Dir.pwd).size}"
|
414
|
+
#empty directory.
|
415
|
+
`wget --output-document=_download_.zip #{url}`
|
416
|
+
raise "Unable to download #{url}" unless $? == 0
|
417
|
+
`unzip _download_.zip`
|
418
|
+
`rm _download_.zip`
|
419
|
+
end
|
420
|
+
VMC::KNIFE::HELPER.static_upload_app_bits(@client,@application_json['name'],Dir.pwd)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def start()
|
426
|
+
raise "The application #{@application_json['name']} does not exist yet" if current().empty?
|
427
|
+
return if current[:state] == 'STARTED'
|
428
|
+
current[:state] = 'STARTED'
|
429
|
+
client.update_app(@application_json['name'], current())
|
430
|
+
end
|
431
|
+
|
432
|
+
def stop()
|
433
|
+
raise "The application #{@application_json['name']} does not exist yet" if current().empty?
|
434
|
+
return if current[:state] == 'STOPPED'
|
435
|
+
current[:state] = 'STOPPED'
|
436
|
+
client.update_app(@application_json['name'], current())
|
437
|
+
end
|
438
|
+
|
439
|
+
# Generate the updated application manifest:
|
440
|
+
# take the manifest defined in the saas recipe
|
441
|
+
# merge it with the current manifest of the application.
|
442
|
+
def updated_manifest()
|
443
|
+
new_app_manifest = JSON.parse(current.to_json) # a deep clone.
|
444
|
+
#now let's update everything.
|
445
|
+
new_mem = @application_json['resources']['memory'] unless @application_json['resources'].nil?
|
446
|
+
new_app_manifest[:name] = @application_json['name']
|
447
|
+
new_app_manifest['resources'] = Hash.new if new_app_manifest['resources'].nil?
|
448
|
+
new_app_manifest['resources']['memory'] = new_mem unless new_mem.nil?
|
449
|
+
unless @application_json['staging'].nil?
|
450
|
+
new_app_manifest['staging'] = Hash.new if new_app_manifest['staging'].nil?
|
451
|
+
new_app_manifest['staging']['model'] = @application_json['staging']['model'] unless @application_json['staging']['model'].nil?
|
452
|
+
#new_app_manifest['staging']['framework'] = new_app_manifest['staging']['model']
|
453
|
+
new_app_manifest['staging']['stack'] = @application_json['staging']['stack'] unless @application_json['staging']['stack'].nil?
|
454
|
+
end
|
455
|
+
new_app_manifest['uris'] = @application_json['uris'] unless @application_json['uris'].nil?
|
456
|
+
new_app_manifest['services'] = @application_json['services'] unless @application_json['services'].nil?
|
457
|
+
new_app_manifest['env'] = @application_json['env'] unless @application_json['env'].nil?
|
458
|
+
new_app_manifest
|
459
|
+
end
|
460
|
+
|
461
|
+
# Returns a json object where we see the differences.
|
462
|
+
def updates_pending()
|
463
|
+
name = update_name_pending()
|
464
|
+
services = update_services_pending()
|
465
|
+
env = update_env_pending()
|
466
|
+
memory = update_memory_pending()
|
467
|
+
uris = update_uris_pending()
|
468
|
+
updates = Hash.new
|
469
|
+
updates['name'] = name if name
|
470
|
+
updates['services'] = services if services
|
471
|
+
updates['env'] = services if services
|
472
|
+
updates['uris'] = uris if uris
|
473
|
+
updates['services'] = services if services
|
474
|
+
updates unless updates.empty?
|
475
|
+
end
|
476
|
+
|
477
|
+
def update_name_pending()
|
478
|
+
if current['name'].nil?
|
479
|
+
return "Create #{@application_json['name']}"
|
480
|
+
end
|
481
|
+
if @application_json['name'] != @current['name']
|
482
|
+
return "#{@current['name']} => #{@application_json['name']}"
|
483
|
+
end
|
484
|
+
end
|
485
|
+
def update_memory_pending()
|
486
|
+
old_mem = current['resources']['memory'] unless current['resources'].nil?
|
487
|
+
new_mem = @application_json['resources']['memory'] unless @application_json['resources'].nil?
|
488
|
+
if old_mem != new_mem
|
489
|
+
return "#{old_mem} => #{new_mem}"
|
490
|
+
end
|
491
|
+
end
|
492
|
+
def update_staging_pending()
|
493
|
+
old_model = current['staging']['model'] unless current['staging'].nil?
|
494
|
+
old_stack = current['staging']['stack'] unless current['staging'].nil?
|
495
|
+
new_model = @application_json['staging']['model'] unless @application_json['staging'].nil?
|
496
|
+
new_stack = @application_json['staging']['stack'] unless @application_json['staging'].nil?
|
497
|
+
if old_model != new_model
|
498
|
+
model_change "#{old_model} => #{new_model}"
|
499
|
+
end
|
500
|
+
if old_stack != new_stack
|
501
|
+
stack_change "#{old_stack} => #{new_stack}"
|
502
|
+
end
|
503
|
+
return if model_change.nil? && stack_change.nil?
|
504
|
+
return { "stack" => stack_change } if model_change.empty?
|
505
|
+
return { "model" => model_change } if stack_change.empty?
|
506
|
+
return { "stack" => stack_change, "model" => model_change }
|
507
|
+
end
|
508
|
+
def update_services_pending()
|
509
|
+
old_services = current['services']
|
510
|
+
new_services = @application_json['services']
|
511
|
+
diff_lists(old_services,new_services)
|
512
|
+
end
|
513
|
+
def update_env_pending()
|
514
|
+
old_services = current['env']
|
515
|
+
new_services = @application_json['env']
|
516
|
+
diff_lists(old_services,new_services)
|
517
|
+
end
|
518
|
+
def update_uris_pending()
|
519
|
+
old_services = current['uris']
|
520
|
+
new_services = @application_json['uris']
|
521
|
+
diff_lists(old_services,new_services)
|
522
|
+
end
|
523
|
+
def diff_lists(old_services,new_services)
|
524
|
+
new_services ||= Array.new
|
525
|
+
old_services ||= Array.new
|
526
|
+
add = Array.new
|
527
|
+
remove = Array.new
|
528
|
+
new_services.each do |item|
|
529
|
+
add << item unless old_services.include? item
|
530
|
+
end
|
531
|
+
old_services.each do |item|
|
532
|
+
remove << item unless new_services.include? item
|
533
|
+
end
|
534
|
+
return if add.empty? && remove.empty?
|
535
|
+
return { "add" => add } if remove.empty?
|
536
|
+
return { "remove" => remove } if add.empty?
|
537
|
+
return { "add" => add, "remove" => remove }
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
# This is really a server-side vcap admin feature.
|
542
|
+
class VCAPUpdateCloudControllerConfig
|
543
|
+
def initialize(uri, cloud_controller_config=nil)
|
544
|
+
@config = cloud_controller_config
|
545
|
+
@config ||="#{ENV['HOME']}/cloudfoundry/config/cloud_controller.yml"
|
546
|
+
@uri = uri
|
547
|
+
raise "The config file #{@config} does not exist." unless File.exists? @config
|
548
|
+
end
|
549
|
+
def update_pending()
|
550
|
+
res = false
|
551
|
+
File.open(@config, "r") do |file|
|
552
|
+
file.each_line do |s|
|
553
|
+
if /^[\s]*external_uri:/ =~ s
|
554
|
+
res = true unless /#{@uri}[\s]*$/ =~ s
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
return res
|
559
|
+
end
|
560
|
+
# look for
|
561
|
+
# cloud_controller_uri: http://api.intalio.me
|
562
|
+
# and replace it with the new one.
|
563
|
+
def update_gateway(config)
|
564
|
+
changed = false
|
565
|
+
lines = IO.readlines config
|
566
|
+
File.open(config, "w") do |file|
|
567
|
+
lines.each do |s|
|
568
|
+
if /^[\s]*cloud_controller_uri:/ =~ s
|
569
|
+
changed = true unless /#{@uri}[\s]*$/ =~ s
|
570
|
+
file.puts "cloud_controller_uri: http://#{@uri}\n"
|
571
|
+
else
|
572
|
+
file.puts s
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
changed
|
577
|
+
end
|
578
|
+
def execute()
|
579
|
+
@changed = false
|
580
|
+
@changed_gateways = Array.new
|
581
|
+
# look for the line that starts with external_uri:
|
582
|
+
# replace it with the new uri if indeed there was a change.
|
583
|
+
lines = IO.readlines @config
|
584
|
+
File.open(@config, "w") do |file|
|
585
|
+
lines.each do |s|
|
586
|
+
if /^[\s]*external_uri:/ =~ s
|
587
|
+
@changed = true unless /#{@uri}[\s]*$/ =~ s
|
588
|
+
file.puts "external_uri: #{@uri}\n"
|
589
|
+
else
|
590
|
+
file.puts s
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
if @changed
|
595
|
+
#update the gateways too.
|
596
|
+
@changed_gateways = Array.new
|
597
|
+
yaml_files = Dir.glob File.join(File.dirname(@config), "*_gateway.yml")
|
598
|
+
yaml_files.each do |path|
|
599
|
+
if update_gateway(path)
|
600
|
+
gateway = File.basename(path, '.yml')
|
601
|
+
@changed_gateways << gateway
|
602
|
+
end
|
603
|
+
end
|
604
|
+
cc_yml = File.open( @config ) { |yf| YAML::load( yf ) }
|
605
|
+
pid = cc_yml['pid']
|
606
|
+
if pid!=nil && File.exists?(pid)
|
607
|
+
display "Restarting the reconfigured cloud_controller"
|
608
|
+
#assuming that the vcap symlink is in place. maker sure the aliases
|
609
|
+
# will be resolved.
|
610
|
+
#`shopt -s expand_aliases; vcap restart cloud_controller`
|
611
|
+
#TODO: something that looks nicer?
|
612
|
+
system ". ~/.bashrc;\n vcap restart cloud_controller"
|
613
|
+
@changed_gateways.each do |gateway|
|
614
|
+
system ". ~/.bashrc;\n vcap restart #{gateway}"
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
def was_changed()
|
620
|
+
@changed
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
# This is really a server-side feature.
|
625
|
+
# Replace the 127.0.0.1 localhost #{old_uri} with the new uri
|
626
|
+
class VCAPUpdateEtcHosts
|
627
|
+
def initialize(uri, etc_hosts_path=nil)
|
628
|
+
@config = etc_hosts_path
|
629
|
+
@config ||="/etc/hosts"
|
630
|
+
@uri = uri
|
631
|
+
raise "The config file #{@config} does not exist." unless File.exists? @config
|
632
|
+
end
|
633
|
+
def update_pending()
|
634
|
+
#could also use:
|
635
|
+
found_it=`sed -n '/^127\.0\.0\.1[[:space:]]*localhost[[:space:]]*#{@uri}/p' #{@config}`
|
636
|
+
return true unless found_it && found_it.strip.length != 0
|
637
|
+
return false
|
638
|
+
end
|
639
|
+
def execute()
|
640
|
+
return unless update_pending
|
641
|
+
@changed = false
|
642
|
+
# look for the line that starts with external_uri:
|
643
|
+
# replace it with the new uri if indeed there was a change.
|
644
|
+
if true
|
645
|
+
# use sudo.
|
646
|
+
puts "Executing sudo sed -i 's/^127\.0\.0\.1[[:space:]]*localhost.*$/127.0.0.1 localhost #{@uri}/g' #{@config}"
|
647
|
+
`sudo sed -i 's/^127\.0\.0\.1[[:space:]]*localhost.*$/127.0.0.1 localhost #{@uri}/g' #{@config}`
|
648
|
+
else
|
649
|
+
lines = IO.readlines @config
|
650
|
+
File.open(@config, "w") do |file|
|
651
|
+
lines.each do |s|
|
652
|
+
if /^127.0.0.1[\s]+localhost[\s]*/ =~ s
|
653
|
+
@changed = true unless /^127.0.0.1[\s]+localhost[\s]+#{@uri}[\s]*/ =~ s
|
654
|
+
file.puts "127.0.0.1\tlocalhost #{@uri}\n"
|
655
|
+
else
|
656
|
+
file.puts s
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
end
|
661
|
+
#Edit the /etc/hostname file: (not a necessity so far).
|
662
|
+
#`sudo hostname #{@uri}`
|
663
|
+
end
|
664
|
+
def was_changed()
|
665
|
+
@changed
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
# This is really a server-side feature.
|
670
|
+
# Regenerates the urls to publish as aliases.
|
671
|
+
# use vmc apps to read the uris of each app and also the manifest.
|
672
|
+
class VCAPUpdateAvahiAliases
|
673
|
+
attr_accessor :do_exec
|
674
|
+
def initialize(avahi_aliases_path=nil, manifest_path=nil,client=nil)
|
675
|
+
@manifest_path = manifest_path
|
676
|
+
@client = client
|
677
|
+
@config = avahi_aliases_path
|
678
|
+
@config ||= '/etc/avahi/aliases'
|
679
|
+
end
|
680
|
+
def apps_uris()
|
681
|
+
return @apps_uris unless @apps_uris.nil?
|
682
|
+
uris = Array.new
|
683
|
+
return uris unless @client
|
684
|
+
apps = @client.apps
|
685
|
+
api_uri = URI.parse(@client.target).host
|
686
|
+
uris << api_uri if /\.local$/ =~ api_uri
|
687
|
+
apps.each do |app|
|
688
|
+
app[:uris].each do |uri|
|
689
|
+
#only publish the uris in the local domain.
|
690
|
+
uris << uri if /\.local$/ =~ uri
|
691
|
+
end
|
692
|
+
end
|
693
|
+
uris.uniq!
|
694
|
+
uris.sort!
|
695
|
+
@apps_uris = uris
|
696
|
+
@apps_uris
|
697
|
+
end
|
698
|
+
def manifest_uris()
|
699
|
+
uris = Array.new
|
700
|
+
return uris unless @manifest_path
|
701
|
+
root = Root.new @manifest_path
|
702
|
+
root.recipes.each do |recipe|
|
703
|
+
recipe.applications.each do |application|
|
704
|
+
application.uris.each do |uri|
|
705
|
+
uris << uri if /\.local$/ =~ uri
|
706
|
+
end
|
707
|
+
end
|
708
|
+
end
|
709
|
+
uris.uniq!
|
710
|
+
uris.sort!
|
711
|
+
uris
|
712
|
+
end
|
713
|
+
def all_uris()
|
714
|
+
uris = manifest_uris+apps_uris
|
715
|
+
uris.uniq!
|
716
|
+
uris.sort!
|
717
|
+
uris
|
718
|
+
end
|
719
|
+
def already_published_uris()
|
720
|
+
already = IO.readlines @config
|
721
|
+
already.collect! {|item| item.strip}
|
722
|
+
already.select! {|item| !item.empty?}
|
723
|
+
already.uniq!
|
724
|
+
already.sort!
|
725
|
+
already
|
726
|
+
end
|
727
|
+
def execute()
|
728
|
+
return unless update_pending()
|
729
|
+
File.open(@config, "w") do |file|
|
730
|
+
all_uris().each do |uri|
|
731
|
+
file.puts uri + "\n"
|
732
|
+
end
|
733
|
+
end
|
734
|
+
#configured so that we don't need root privileges on /etc/avahi/aliases:
|
735
|
+
#the backticks don't work; system() works:
|
736
|
+
system('avahi-publish-aliases') if @do_exec
|
737
|
+
end
|
738
|
+
def update_pending()
|
739
|
+
already = already_published_uris()
|
740
|
+
length_already = already.length
|
741
|
+
allall = already + all_uris()
|
742
|
+
allall.uniq!
|
743
|
+
return length_already != allall.length
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
end # end of KNIFE
|
748
|
+
end
|