vmc_knife 0.0.01
Sign up to get free protection for your applications and to get access to all the features.
- 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
|