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
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2010-2011 Intalio Pte, All Rights Reserved
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
21
|
+
This software downloads additional open source software components upon install
|
22
|
+
that are distributed under separate terms and conditions. Please see the license
|
23
|
+
information provided in the individual software components for more information.
|
24
|
+
|
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# VMC Knife
|
2
|
+
Extensions for vmc: the VMware Cloud CLI; the command line interface to VMware's Application Platform
|
3
|
+
|
4
|
+
MIT license
|
5
|
+
|
6
|
+
## Usecase
|
7
|
+
Prepare a file that describes the list of applications to deploy, their settings, their environment variables, required data-services and names, build file for the apps.
|
8
|
+
Run the deployment file on the command: will either install update, modify the apps on the cloudfoundry.
|
9
|
+
Run the deployment file via a web-interface.
|
10
|
+
|
11
|
+
prepare a vmc deployment file.
|
12
|
+
vmc_knife vmc_deployment_file.json
|
13
|
+
|
14
|
+
|
data/bin/vmc_knife
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
# Make sure the rest-client won't time out too quickly:
|
4
|
+
# vmc uses the rest-client for its rest calls but does not let us configure the timeouts.
|
5
|
+
# We monkey patch here.
|
6
|
+
module RestClient
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :timeout
|
10
|
+
attr_accessor :open_timeout
|
11
|
+
end
|
12
|
+
|
13
|
+
class Request
|
14
|
+
|
15
|
+
def self.execute(args, &block)
|
16
|
+
#puts "Calling overriden RestClient::Request execute"
|
17
|
+
timeouts = {:timeout=>90000000, :open_timeout=>90000000}
|
18
|
+
args.merge!(timeouts)
|
19
|
+
#puts "Req args #{args}"
|
20
|
+
new(args).execute(& block)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.post(url, payload, headers={}, &block)
|
26
|
+
Request.execute(:method => :post,
|
27
|
+
:url => url,
|
28
|
+
:payload => payload,
|
29
|
+
:headers => headers,
|
30
|
+
:timeout=>@timeout,
|
31
|
+
:open_timeout=>@open_timeout,
|
32
|
+
&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
#RestClient.open_timeout = 90000000
|
39
|
+
#RestClient.timeout = 90000000
|
40
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'cli' #this is the cli from vmc.
|
3
|
+
|
4
|
+
# Adds some new commands to the vmc's cli.
|
5
|
+
#
|
6
|
+
# Reconfigure applications according to a saas recipe.
|
7
|
+
# The SaaS recipe is a json object that contains the manifest of each application.
|
8
|
+
# as well as a short declaration of the services expected and their nature.
|
9
|
+
# Usage: edit the json recipe.
|
10
|
+
# vmc_knife configure-applications
|
11
|
+
#
|
12
|
+
# Also bundles utilities to reconfigure the hostname of the cloud_controller and the gateways accordingly:
|
13
|
+
# vmc_knife configure-vcap
|
14
|
+
# and publish the urls in the deployed apps with zeroconf on ubuntu (avahi)
|
15
|
+
# vmc configure-vcap-mdns
|
16
|
+
class VMC::Cli::KnifeRunner < VMC::Cli::Runner
|
17
|
+
|
18
|
+
def parse_command!
|
19
|
+
# just return if already set, happends with -v, -h
|
20
|
+
return if @namespace && @action
|
21
|
+
|
22
|
+
verb = @args.first
|
23
|
+
case verb
|
24
|
+
|
25
|
+
when 'expand-manifest'
|
26
|
+
usage('vmc_knife expand-manifest <path_to_json_manifest> <path_to_destination_expanded_manifest>')
|
27
|
+
@args.shift # consumes the argument.
|
28
|
+
if @args.size == 1
|
29
|
+
set_cmd(:knife, :expand_manifest, 1)
|
30
|
+
elsif @args.size == 2
|
31
|
+
set_cmd(:knife, :expand_manifest, 2)
|
32
|
+
else
|
33
|
+
set_cmd(:knife, :expand_manifest)
|
34
|
+
end
|
35
|
+
when 'login', 'target'
|
36
|
+
usage('vmc_knife login [<path_to_json_manifest>]')
|
37
|
+
@args.shift # consumes the argument.
|
38
|
+
if @args.size == 1
|
39
|
+
set_cmd(:knifemisc, :login, 1)
|
40
|
+
else
|
41
|
+
set_cmd(:knifemisc, :login)
|
42
|
+
end
|
43
|
+
when 'configure-all'
|
44
|
+
usage('vmc_knife configure-all [<path_to_cloud_controller_config_yml>] [<path_to_json_manifest_or_uri>]')
|
45
|
+
@args.shift # consumes the argument.
|
46
|
+
if @args.size <= 2
|
47
|
+
set_cmd(:knife, :configure_all, @args.size)
|
48
|
+
else
|
49
|
+
set_cmd(:knife, :configure_all, @args.size) # too many
|
50
|
+
end
|
51
|
+
when 'configure-vcap'
|
52
|
+
usage('vmc_knife configure-vcap [<path_to_cloud_controller_config_yml>] [<path_to_json_manifest_or_uri>]')
|
53
|
+
@args.shift # consumes the argument.
|
54
|
+
if @args.size <= 2
|
55
|
+
set_cmd(:knife, :configure_cloud_controller, @args.size)
|
56
|
+
else
|
57
|
+
set_cmd(:knife, :configure_cloud_controller, @args.size) # too many
|
58
|
+
end
|
59
|
+
when 'configure-vcap-etc-hosts'
|
60
|
+
usage('vmc_knife configure-vap-etc-hosts [<path_to_etc_hosts>] [<path_to_json_manifest_or_uri>]')
|
61
|
+
@args.shift # consumes the argument.
|
62
|
+
if @args.size <= 2
|
63
|
+
set_cmd(:knife, :configure_etc_hosts, @args.size)
|
64
|
+
else
|
65
|
+
set_cmd(:knife, :configure_etc_hosts, @args.size) # too many
|
66
|
+
end
|
67
|
+
when 'configure-vcap-mdns'
|
68
|
+
usage('vmc_knife configure-vap-mdns [<path_to_aliases>]')
|
69
|
+
@args.shift # consumes the argument.
|
70
|
+
if @args.size <= 2
|
71
|
+
set_cmd(:knife, :configure_etc_avahi_aliases, @args.size)
|
72
|
+
else
|
73
|
+
set_cmd(:knife, :configure_etc_avahi_aliases, @args.size) # too many
|
74
|
+
end
|
75
|
+
when 'configure-applications'
|
76
|
+
usage('vmc_knife configure-applications [<applications_regexp>]')
|
77
|
+
@args.shift # consumes the argument.
|
78
|
+
if @args.size <= 2
|
79
|
+
set_cmd(:knifeapps, :configure_applications, @args.size)
|
80
|
+
else
|
81
|
+
set_cmd(:knifeapps, :configure_applications, @args.size) # too many
|
82
|
+
end
|
83
|
+
when 'configure-services'
|
84
|
+
usage('vmc_knife configure-services [<services_regexp>]')
|
85
|
+
@args.shift # consumes the argument.
|
86
|
+
if @args.size <= 2
|
87
|
+
set_cmd(:knifeapps, :configure_services, @args.size)
|
88
|
+
else
|
89
|
+
set_cmd(:knifeapps, :configure_services, @args.size) # too many
|
90
|
+
end
|
91
|
+
when 'configure-recipes'
|
92
|
+
usage('vmc_knife configure-recipes [<recipes_regexp>]')
|
93
|
+
@args.shift # consumes the argument.
|
94
|
+
if @args.size <= 2
|
95
|
+
set_cmd(:knifeapps, :configure_recipes, @args.size)
|
96
|
+
else
|
97
|
+
set_cmd(:knifeapps, :configure_recipes, @args.size) # too many
|
98
|
+
end
|
99
|
+
when 'upload-applications'
|
100
|
+
usage('vmc_knife upload-applications [<applications_regexp>]')
|
101
|
+
@args.shift # consumes the argument.
|
102
|
+
if @args.size <= 2
|
103
|
+
set_cmd(:knifeapps, :upload_applications, @args.size)
|
104
|
+
else
|
105
|
+
set_cmd(:knifeapps, :upload_applications, @args.size) # too many
|
106
|
+
end
|
107
|
+
when 'start-applications'
|
108
|
+
usage('vmc_knife start-applications [<applications_regexp>]')
|
109
|
+
@args.shift # consumes the argument.
|
110
|
+
if @args.size <= 2
|
111
|
+
set_cmd(:knifeapps, :start_applications, @args.size)
|
112
|
+
else
|
113
|
+
set_cmd(:knifeapps, :start_applications, @args.size) # too many
|
114
|
+
end
|
115
|
+
when 'stop-applications'
|
116
|
+
usage('vmc_knife stop-applications [<applications_regexp>]')
|
117
|
+
@args.shift # consumes the argument.
|
118
|
+
if @args.size <= 2
|
119
|
+
set_cmd(:knifeapps, :stop_applications, @args.size)
|
120
|
+
else
|
121
|
+
set_cmd(:knifeapps, :stop_applications, @args.size) # too many
|
122
|
+
end
|
123
|
+
when 'restart-applications'
|
124
|
+
usage('vmc_knife restart-applications [<applications_regexp>]')
|
125
|
+
@args.shift # consumes the argument.
|
126
|
+
if @args.size <= 2
|
127
|
+
set_cmd(:knifeapps, :restart_applications, @args.size)
|
128
|
+
else
|
129
|
+
set_cmd(:knifeapps, :restart_applications, @args.size) # too many
|
130
|
+
end
|
131
|
+
when 'help'
|
132
|
+
display "vmc_knife expand-manifest|login|start/stop/restart-applications|upload-applications|configure|configure-recipes|configure-applications|configure-services|configure-vcap|configure-vcap-mdns|configure-vcap-etc-hosts [<manifest_path>]"
|
133
|
+
else
|
134
|
+
super
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# Commands for vmc_knife.
|
2
|
+
|
3
|
+
module VMC::KNIFE::Cli
|
4
|
+
|
5
|
+
#loads the manifest file.
|
6
|
+
#when the path is not specified, look in the current directory.
|
7
|
+
#when the path is a directory, look for the first json file it can find.
|
8
|
+
#the json file actually loaded is set as the attribute @manifest_path
|
9
|
+
def load_manifest(manifest_file_path=nil)
|
10
|
+
manifest_file_path = Dir.pwd if manifest_file_path.nil?
|
11
|
+
if File.directory? manifest_file_path
|
12
|
+
#look for the first .json file if possible that is not an expanded.json
|
13
|
+
Dir.glob(File.join(manifest_file_path,"*.json")).each do |file|
|
14
|
+
@manifest_path = file
|
15
|
+
if VMC::Cli::Config.trace
|
16
|
+
display "Using the manifest #{@manifest_path}"
|
17
|
+
end
|
18
|
+
return VMC::KNIFE::JSON_EXPANDER.expand_json @manifest_path
|
19
|
+
end
|
20
|
+
raise "Unable to find a *.json file in #{manifest_file_path}"
|
21
|
+
else
|
22
|
+
@manifest_path = manifest_file_path
|
23
|
+
return VMC::KNIFE::JSON_EXPANDER.expand_json @manifest_path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module VMC::Cli::Command
|
29
|
+
|
30
|
+
class Knife < Base
|
31
|
+
include VMC::KNIFE::Cli
|
32
|
+
|
33
|
+
# expands the json manifest. outputs it in the destination path.
|
34
|
+
def expand_manifest(manifest_file_path, destination=nil)
|
35
|
+
res = VMC::KNIFE::JSON_EXPANDER.expand_json manifest_file_path
|
36
|
+
if destination.nil?
|
37
|
+
noextension = File.basename(manifest_file_path, File.extname(manifest_file_path))
|
38
|
+
destination = File.join File.dirname(manifest_file_path), "#{noextension}.expanded.json"
|
39
|
+
end
|
40
|
+
display "Expanding the manifest #{manifest_file_path} into #{destination}"
|
41
|
+
if VMC::Cli::Config.trace
|
42
|
+
display JSON.pretty_generate(res)
|
43
|
+
end
|
44
|
+
File.open(destination, 'w') {|f| f.write(JSON.pretty_generate(res)) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# updates the cloud_controller
|
48
|
+
def configure_cloud_controller(cloud_controller_yml=nil,manifest_file_path_or_uri=nil)
|
49
|
+
__update(manifest_file_path_or_uri,cloud_controller_yml,VMC::KNIFE::VCAPUpdateCloudControllerConfig,"cloud_controller")
|
50
|
+
end
|
51
|
+
# updates /etc/hosts
|
52
|
+
def configure_etc_hosts(etc_hosts=nil,manifest_file_path_or_uri=nil)
|
53
|
+
__update(manifest_file_path_or_uri,etc_hosts,VMC::KNIFE::VCAPUpdateEtcHosts,"/etc/hosts")
|
54
|
+
end
|
55
|
+
# updates /etc/avahi/aliases
|
56
|
+
def configure_etc_avahi_aliases(etc_avahi_aliases=nil,manifest_file_path=nil)
|
57
|
+
man = load_manifest(manifest_file_path)
|
58
|
+
update_aliases = VMC::KNIFE::VCAPUpdateAvahiAliases.new(etc_avahi_aliases,man,client)
|
59
|
+
update_aliases.do_exec = true
|
60
|
+
update_aliases.execute
|
61
|
+
end
|
62
|
+
|
63
|
+
def configure_all(manifest_file_path_or_uri=nil)
|
64
|
+
display "Stop applications ..."
|
65
|
+
VMC::Cli::Command::Knifeapps.new(@options).stop_applications(nil,manifest_file_path_or_uri)
|
66
|
+
display "Configure_cloud_controller ..."
|
67
|
+
configure_cloud_controller(nil,manifest_file_path_or_uri)
|
68
|
+
display "Configure_etc_hosts ..."
|
69
|
+
configure_etc_hosts(nil,manifest_file_path_or_uri)
|
70
|
+
display "Login again ..."
|
71
|
+
VMC::Cli::Command::Knifemisc.new(@options).login(manifest_file_path_or_uri)
|
72
|
+
display "Configure_applications ..."
|
73
|
+
VMC::Cli::Command::Knifeapps.new(@options).configure_applications(nil,manifest_file_path_or_uri)
|
74
|
+
display "Configure_etc_avahi_aliases ..."
|
75
|
+
configure_etc_avahi_aliases(nil,manifest_file_path_or_uri)
|
76
|
+
display "Start applications ..."
|
77
|
+
VMC::Cli::Command::Knifeapps.new(@options).start_applications(nil,manifest_file_path_or_uri)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def __update(manifest_file_path_or_uri,config,_class,msg_label)
|
82
|
+
unless manifest_file_path_or_uri.nil?
|
83
|
+
if File.exists? manifest_file_path_or_uri
|
84
|
+
man = load_manifest(manifest_file_path_or_uri)
|
85
|
+
uri = man['target']
|
86
|
+
else
|
87
|
+
uri = manifest_file_path_or_uri
|
88
|
+
end
|
89
|
+
else
|
90
|
+
man = load_manifest(nil)
|
91
|
+
uri = man['target']
|
92
|
+
end
|
93
|
+
raise "No uri defined" unless uri
|
94
|
+
update_cc = _class.new(uri,config)
|
95
|
+
if update_cc.update_pending()
|
96
|
+
display "Configuring #{msg_label} with uri: #{uri}" if VMC::Cli::Config.trace
|
97
|
+
update_cc.execute()
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
class Knifemisc < Misc
|
104
|
+
include VMC::KNIFE::Cli
|
105
|
+
|
106
|
+
# configures the target and login according to the info in the manifest.
|
107
|
+
def login(manifest_file_path=nil)
|
108
|
+
man = load_manifest(manifest_file_path)
|
109
|
+
target_url = man['target']
|
110
|
+
raise "No target defined in the manifest #{@manifest_path}" if target_url.nil?
|
111
|
+
set_target(target_url)
|
112
|
+
|
113
|
+
email = man['email']
|
114
|
+
password = man['password']
|
115
|
+
@options[:email] = email if email
|
116
|
+
@options[:password] = password if password
|
117
|
+
|
118
|
+
tries ||= 0
|
119
|
+
# login_and_save_token:
|
120
|
+
token = client.login(email, password)
|
121
|
+
VMC::Cli::Config.store_token(token)
|
122
|
+
|
123
|
+
rescue VMC::Client::TargetError
|
124
|
+
display "Problem with login, invalid account or password.".red
|
125
|
+
retry if (tries += 1) < 3 && prompt_ok && !@options[:password]
|
126
|
+
exit 1
|
127
|
+
rescue => e
|
128
|
+
display "Problem with login, #{e}, try again or register for an account.".red
|
129
|
+
display e.backtrace
|
130
|
+
exit 1
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
class Knifeapps < Apps
|
137
|
+
include VMC::KNIFE::Cli
|
138
|
+
|
139
|
+
def configure_applications(app_names_regexp=nil,manifest_file_path=nil)
|
140
|
+
configure(nil,nil,app_names_regexp,manifest_file_path)
|
141
|
+
end
|
142
|
+
def configure_services(services_names_regexp=nil,manifest_file_path=nil)
|
143
|
+
configure(nil,nil,services_names_regexp,manifest_file_path)
|
144
|
+
end
|
145
|
+
def configure_recipes(recipe_names_regexp=nil,manifest_file_path=nil)
|
146
|
+
configure(recipe_names_regexp,nil,nil,manifest_file_path)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Configure the applications according to their manifest.
|
150
|
+
# The parameters are related to selecting a subset of the applications to configure.
|
151
|
+
# nil means all apps for all recipes found in the manifest are configured.
|
152
|
+
# @param recipes The list of recipes: nil: search the apps in all recipes
|
153
|
+
# @param app_role_names The names of the apps in each recipe; nil: configure all apps found.
|
154
|
+
def configure(recipes_regexp=nil,app_names_regexp=nil,service_names_regexp=nil,manifest_file_path=nil)
|
155
|
+
man = load_manifest(manifest_file_path)
|
156
|
+
configurer = VMC::KNIFE::RecipesConfigurationApplier.new(man,client,recipes_regexp,app_names_regexp,service_names_regexp)
|
157
|
+
if VMC::Cli::Config.trace
|
158
|
+
display "Pending updates"
|
159
|
+
display JSON.pretty_generate(configurer.updates_pending)
|
160
|
+
end
|
161
|
+
configurer.execute
|
162
|
+
end
|
163
|
+
|
164
|
+
def upload_applications(app_names_regexp=nil,manifest_file_path=nil)
|
165
|
+
recipe_configuror(:upload,nil,nil,app_names_regexp,manifest_file_path)
|
166
|
+
end
|
167
|
+
def start_applications(app_names_regexp=nil,manifest_file_path=nil)
|
168
|
+
recipe_configuror(:start,nil,nil,app_names_regexp,manifest_file_path)
|
169
|
+
end
|
170
|
+
def stop_applications(app_names_regexp=nil,manifest_file_path=nil)
|
171
|
+
recipe_configuror(:stop,nil,nil,app_names_regexp,manifest_file_path)
|
172
|
+
end
|
173
|
+
def restart_applications(app_names_regexp=nil,manifest_file_path=nil)
|
174
|
+
recipe_configuror(:restart,nil,nil,app_names_regexp,manifest_file_path)
|
175
|
+
end
|
176
|
+
|
177
|
+
def recipe_configuror(method_sym_name,recipes_regexp=nil,app_names_regexp=nil,service_names_regexp=nil,manifest_file_path=nil)
|
178
|
+
man = load_manifest(manifest_file_path)
|
179
|
+
configurer = VMC::KNIFE::RecipesConfigurationApplier.new(man,client,recipes_regexp,app_names_regexp,service_names_regexp)
|
180
|
+
method_object = configurer.method(method_sym_name)
|
181
|
+
method_object.call
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'json'
|
2
|
+
module VMC
|
3
|
+
module KNIFE
|
4
|
+
module JSON_DIFF
|
5
|
+
|
6
|
+
DIFF_NODE_NAME = "__diff__"
|
7
|
+
TYPE_NODE_NAME = "__type__"
|
8
|
+
PLUS="+"
|
9
|
+
MINUS="-"
|
10
|
+
CHANGED="=>"
|
11
|
+
DISPLAY_TYPE_ONLY_WHEN_CHANGE=true
|
12
|
+
|
13
|
+
def self.compare(a,b,pretty=true)
|
14
|
+
if a.kind_of?(String) && b.kind_of?(String) && File.exists?(a) && File.exists?(b)
|
15
|
+
a = JSON.parse File.open(a).read
|
16
|
+
b = JSON.parse File.open(b).read
|
17
|
+
end
|
18
|
+
if pretty
|
19
|
+
JSON.pretty_generate compare_tree(a, b)
|
20
|
+
else
|
21
|
+
compare_tree(a, b).to_json
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.compare_tree(a, b)
|
26
|
+
typeA = a.class.name
|
27
|
+
typeB = b.class.name
|
28
|
+
|
29
|
+
aString = a.to_s unless is_array_or_hash?(a)
|
30
|
+
bString = b.to_s unless is_array_or_hash?(b)
|
31
|
+
|
32
|
+
node = Hash.new
|
33
|
+
if a.nil?
|
34
|
+
node[DIFF_NODE_NAME]=PLUS
|
35
|
+
node[TYPE_NODE_NAME]=typeB unless DISPLAY_TYPE_ONLY_WHEN_CHANGE
|
36
|
+
node['value']=bString if bString
|
37
|
+
elsif b.nil?
|
38
|
+
node[DIFF_NODE_NAME]=MINUS
|
39
|
+
node[TYPE_NODE_NAME]=typeA unless DISPLAY_TYPE_ONLY_WHEN_CHANGE
|
40
|
+
node['value']=aString if aString
|
41
|
+
elsif (typeA != typeB) || (!aString.nil? && a != b)
|
42
|
+
node[DIFF_NODE_NAME]=CHANGED
|
43
|
+
if typeA != typeB
|
44
|
+
node[TYPE_NODE_NAME]="#{typeA} => #{typeB}"
|
45
|
+
else
|
46
|
+
node[TYPE_NODE_NAME]=typeA unless DISPLAY_TYPE_ONLY_WHEN_CHANGE
|
47
|
+
end
|
48
|
+
node['value']="#{aString} => #{bString}" if aString
|
49
|
+
else
|
50
|
+
node[TYPE_NODE_NAME]=typeA unless DISPLAY_TYPE_ONLY_WHEN_CHANGE
|
51
|
+
node['value']=aString if aString
|
52
|
+
end
|
53
|
+
|
54
|
+
if aString
|
55
|
+
return node
|
56
|
+
end
|
57
|
+
child_node=node
|
58
|
+
# child_node = Hash.new
|
59
|
+
# node['child']=child_node
|
60
|
+
keys = Array.new
|
61
|
+
keys = collect_keys(a,keys)
|
62
|
+
keys = collect_keys(b,keys)
|
63
|
+
keys.sort!
|
64
|
+
for i in 0..(keys.length-1)
|
65
|
+
if (keys[i] != keys[i-1])
|
66
|
+
value = compare_tree(a && a[keys[i]], b && b[keys[i]]);
|
67
|
+
child_node[keys[i]]=value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
node
|
71
|
+
end
|
72
|
+
private
|
73
|
+
def self.is_array_or_hash?(obj)
|
74
|
+
obj.kind_of?(Array) || obj.kind_of?(Hash)
|
75
|
+
end
|
76
|
+
def self.collect_keys(obj, collector)
|
77
|
+
return Array.new unless obj.kind_of? Hash
|
78
|
+
collector + obj.keys
|
79
|
+
end
|
80
|
+
|
81
|
+
end # end of JSON_DIFF
|
82
|
+
end # end of KNIFE
|
83
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'vmc/client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module VMC
|
5
|
+
module KNIFE
|
6
|
+
module JSON_EXPANDER
|
7
|
+
|
8
|
+
# Loads a json file.
|
9
|
+
# Makes up to 10 passes evaluating ruby in the values that contain #{}.
|
10
|
+
def self.expand_json(file_path)
|
11
|
+
raise "The file #{file_path} does not exist" unless File.exists? file_path
|
12
|
+
data = JSON.parse File.open(file_path).read
|
13
|
+
#puts "got data #{data.to_json}"
|
14
|
+
passes = 0
|
15
|
+
while passes < 100
|
16
|
+
#puts "pass #{passes}"
|
17
|
+
break unless expand_data(data,data)
|
18
|
+
passes += 1
|
19
|
+
end
|
20
|
+
puts data.to_json unless passes < 100
|
21
|
+
raise "More than 100 passes evaluating the ruby template in the json file" unless passes < 100
|
22
|
+
#puts "got data #{data.to_json}"
|
23
|
+
data
|
24
|
+
end
|
25
|
+
|
26
|
+
# Traverses the JSON object
|
27
|
+
# Eval the values that are strings and contain a #{}
|
28
|
+
# Does not do it recursively
|
29
|
+
# data The root data passed as 'this' in the binding to the eval function
|
30
|
+
# @return true if there was a change.
|
31
|
+
def self.expand_data(data,current)
|
32
|
+
at_least_one_eval = false
|
33
|
+
if current.kind_of? Hash
|
34
|
+
current.each_pair do | k, v |
|
35
|
+
if v.kind_of? String
|
36
|
+
if /\#{.+}/ =~ v
|
37
|
+
at_least_one_eval = true
|
38
|
+
begin
|
39
|
+
evalled = eval_v(v,data,current)
|
40
|
+
current[k] = evalled unless evalled.nil?
|
41
|
+
rescue => e
|
42
|
+
raise "Error thrown evaluating #{v}: #{e.inspect}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
else
|
46
|
+
at_least_one_eval ||= expand_data(data,v)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
elsif current.kind_of? Array
|
50
|
+
index = 0
|
51
|
+
current.each do | v |
|
52
|
+
if v.kind_of? String
|
53
|
+
if /\#{.+}/ =~ v
|
54
|
+
at_least_one_eval = true
|
55
|
+
begin
|
56
|
+
evalled = eval_v(v,data,current)
|
57
|
+
current[index] = evalled unless evalled.nil?
|
58
|
+
rescue => e
|
59
|
+
raise "Error thrown evaluating #{v}: #{e.inspect}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
else
|
63
|
+
at_least_one_eval ||= expand_data(data,v)
|
64
|
+
end
|
65
|
+
index+=1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
at_least_one_eval
|
69
|
+
end
|
70
|
+
|
71
|
+
# internal eval a reference.
|
72
|
+
# the reference is always wrapped in a json string.
|
73
|
+
# however if it is purely a ruby script ("#{ruby here}" ) we unwrap it
|
74
|
+
# to avoid casting the result into a string.
|
75
|
+
def self.eval_v(v,data,current)
|
76
|
+
#puts "evalling #{v}"
|
77
|
+
if /^\#{([^}]*)}$/ =~ v
|
78
|
+
val = $1
|
79
|
+
else
|
80
|
+
val = '"'+v+'"'
|
81
|
+
end
|
82
|
+
evalled = eval(val,get_binding(data,current))
|
83
|
+
#puts "evaluating #{v} => #{evalled} class #{evalled.class.name}"
|
84
|
+
evalled
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.get_binding(this,current)
|
88
|
+
binding
|
89
|
+
end
|
90
|
+
|
91
|
+
end #end of JSON_EXPANDER
|
92
|
+
|
93
|
+
end # end of KNIFE
|
94
|
+
|
95
|
+
end
|