vmc_knife 0.0.01

Sign up to get free protection for your applications and to get access to all the features.
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,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/vmc_knife', __FILE__)
4
+
5
+ VMC::Cli::KnifeRunner.run(ARGV.dup)
6
+
@@ -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
@@ -0,0 +1,8 @@
1
+ module VMC
2
+ module KNIFE
3
+ module Cli
4
+ # This version number is used as the RubyGem release version.
5
+ VERSION = '0.0.01'
6
+ end
7
+ end
8
+ end