vmc_virgo 0.0.1.beta
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 +93 -0
- data/Rakefile +99 -0
- data/bin/vmc +6 -0
- data/caldecott_helper/Gemfile +10 -0
- data/caldecott_helper/Gemfile.lock +48 -0
- data/caldecott_helper/server.rb +43 -0
- data/config/clients.yml +17 -0
- data/lib/cli/commands/admin.rb +80 -0
- data/lib/cli/commands/apps.rb +1104 -0
- data/lib/cli/commands/base.rb +227 -0
- data/lib/cli/commands/manifest.rb +56 -0
- data/lib/cli/commands/misc.rb +129 -0
- data/lib/cli/commands/services.rb +180 -0
- data/lib/cli/commands/user.rb +65 -0
- data/lib/cli/config.rb +160 -0
- data/lib/cli/console_helper.rb +157 -0
- data/lib/cli/core_ext.rb +122 -0
- data/lib/cli/errors.rb +19 -0
- data/lib/cli/frameworks.rb +138 -0
- data/lib/cli/manifest_helper.rb +262 -0
- data/lib/cli/runner.rb +521 -0
- data/lib/cli/services_helper.rb +84 -0
- data/lib/cli/tunnel_helper.rb +332 -0
- data/lib/cli/usage.rb +106 -0
- data/lib/cli/version.rb +8 -0
- data/lib/cli/zip_util.rb +77 -0
- data/lib/cli.rb +33 -0
- data/lib/vmc/client.rb +471 -0
- data/lib/vmc/const.rb +22 -0
- data/lib/vmc.rb +3 -0
- metadata +194 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'interact'
|
3
|
+
require 'terminal-table/import'
|
4
|
+
|
5
|
+
module VMC::Cli
|
6
|
+
|
7
|
+
module Command
|
8
|
+
|
9
|
+
class Base
|
10
|
+
include Interactive
|
11
|
+
|
12
|
+
attr_reader :no_prompt, :prompt_ok
|
13
|
+
|
14
|
+
MANIFEST = "manifest.yml"
|
15
|
+
|
16
|
+
def initialize(options={})
|
17
|
+
@options = options.dup
|
18
|
+
@no_prompt = @options[:noprompts]
|
19
|
+
@prompt_ok = !no_prompt
|
20
|
+
|
21
|
+
# Suppress colorize on Windows systems for now.
|
22
|
+
if WINDOWS
|
23
|
+
VMC::Cli::Config.colorize = false
|
24
|
+
end
|
25
|
+
|
26
|
+
@path = @options[:path] || '.'
|
27
|
+
|
28
|
+
load_manifest manifest_file if manifest_file
|
29
|
+
end
|
30
|
+
|
31
|
+
def manifest_file
|
32
|
+
return @options[:manifest] if @options[:manifest]
|
33
|
+
return @manifest_file if @manifest_file
|
34
|
+
|
35
|
+
where = File.expand_path(@path)
|
36
|
+
while true
|
37
|
+
if File.exists?(File.join(where, MANIFEST))
|
38
|
+
@manifest_file = File.join(where, MANIFEST)
|
39
|
+
break
|
40
|
+
elsif File.basename(where) == "/"
|
41
|
+
@manifest_file = nil
|
42
|
+
break
|
43
|
+
else
|
44
|
+
where = File.expand_path("../", where)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@manifest_file
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_manifest_structure(file)
|
52
|
+
manifest = YAML.load_file file
|
53
|
+
|
54
|
+
Array(manifest["inherit"]).each do |p|
|
55
|
+
manifest = merge_parent(manifest, p)
|
56
|
+
end
|
57
|
+
|
58
|
+
if apps = manifest["applications"]
|
59
|
+
apps.each do |k, v|
|
60
|
+
abs = File.expand_path(k, file)
|
61
|
+
if Dir.pwd.start_with? abs
|
62
|
+
manifest = merge_manifest(manifest, v)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
manifest
|
68
|
+
end
|
69
|
+
|
70
|
+
def resolve_manifest(manifest)
|
71
|
+
if apps = manifest["applications"]
|
72
|
+
apps.each_value do |v|
|
73
|
+
resolve_lexically(v, [manifest])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
resolve_lexically(manifest, [manifest])
|
78
|
+
end
|
79
|
+
|
80
|
+
def load_manifest(file)
|
81
|
+
@manifest = load_manifest_structure(file)
|
82
|
+
resolve_manifest(@manifest)
|
83
|
+
end
|
84
|
+
|
85
|
+
def merge_parent(child, path)
|
86
|
+
file = File.expand_path("../" + path, manifest_file)
|
87
|
+
merge_manifest(child, load_manifest_structure(file))
|
88
|
+
end
|
89
|
+
|
90
|
+
def merge_manifest(child, parent)
|
91
|
+
merge = proc do |_, old, new|
|
92
|
+
if new.is_a?(Hash) and old.is_a?(Hash)
|
93
|
+
old.merge(new, &merge)
|
94
|
+
else
|
95
|
+
new
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
parent.merge(child, &merge)
|
100
|
+
end
|
101
|
+
|
102
|
+
def resolve_lexically(val, ctx = [@manifest])
|
103
|
+
case val
|
104
|
+
when Hash
|
105
|
+
val.each_value do |v|
|
106
|
+
resolve_lexically(v, [val] + ctx)
|
107
|
+
end
|
108
|
+
when Array
|
109
|
+
val.each do |v|
|
110
|
+
resolve_lexically(v, ctx)
|
111
|
+
end
|
112
|
+
when String
|
113
|
+
val.gsub!(/\$\{([[:alnum:]\-]+)\}/) do
|
114
|
+
resolve_symbol($1, ctx)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def resolve_symbol(sym, ctx)
|
122
|
+
case sym
|
123
|
+
when "target-base"
|
124
|
+
target_base(ctx)
|
125
|
+
|
126
|
+
when "target-url"
|
127
|
+
target_url(ctx)
|
128
|
+
|
129
|
+
when "random-word"
|
130
|
+
"%04x" % [rand(0x0100000)]
|
131
|
+
|
132
|
+
else
|
133
|
+
found = find_symbol(sym, ctx)
|
134
|
+
|
135
|
+
if found
|
136
|
+
resolve_lexically(found, ctx)
|
137
|
+
found
|
138
|
+
else
|
139
|
+
err(sym, "Unknown symbol in manifest: ")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def find_symbol(sym, ctx)
|
145
|
+
ctx.each do |h|
|
146
|
+
if val = resolve_in(h, sym)
|
147
|
+
return val
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
def resolve_in(hash, *where)
|
155
|
+
find_in_hash(hash, ["properties"] + where) ||
|
156
|
+
find_in_hash(hash, ["applications", @application] + where) ||
|
157
|
+
find_in_hash(hash, where)
|
158
|
+
end
|
159
|
+
|
160
|
+
def manifest(*where)
|
161
|
+
resolve_in(@manifest, *where)
|
162
|
+
end
|
163
|
+
|
164
|
+
def find_in_hash(hash, where)
|
165
|
+
what = hash
|
166
|
+
where.each do |x|
|
167
|
+
return nil unless what.is_a?(Hash)
|
168
|
+
what = what[x]
|
169
|
+
end
|
170
|
+
|
171
|
+
what
|
172
|
+
end
|
173
|
+
|
174
|
+
def target_url(ctx = [])
|
175
|
+
find_symbol("target", ctx) ||
|
176
|
+
(@client && @client.target) ||
|
177
|
+
VMC::Cli::Config.target_url
|
178
|
+
end
|
179
|
+
|
180
|
+
def target_base(ctx = [])
|
181
|
+
VMC::Cli::Config.base_of(find_symbol("target", ctx) || target_url)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Inject a client to help in testing.
|
185
|
+
def client(cli=nil)
|
186
|
+
@client ||= cli
|
187
|
+
return @client if @client
|
188
|
+
@client = VMC::Client.new(target_url, auth_token)
|
189
|
+
@client.trace = VMC::Cli::Config.trace if VMC::Cli::Config.trace
|
190
|
+
@client.proxy_for @options[:proxy] if @options[:proxy]
|
191
|
+
@client
|
192
|
+
end
|
193
|
+
|
194
|
+
def client_info
|
195
|
+
@client_info ||= client.info
|
196
|
+
end
|
197
|
+
|
198
|
+
def auth_token
|
199
|
+
@auth_token = VMC::Cli::Config.auth_token(@options[:token_file])
|
200
|
+
end
|
201
|
+
|
202
|
+
def runtimes_info
|
203
|
+
return @runtimes if @runtimes
|
204
|
+
info = client_info
|
205
|
+
@runtimes = {}
|
206
|
+
if info[:frameworks]
|
207
|
+
info[:frameworks].each_value do |f|
|
208
|
+
next unless f[:runtimes]
|
209
|
+
f[:runtimes].each { |r| @runtimes[r[:name]] = r}
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@runtimes
|
213
|
+
end
|
214
|
+
|
215
|
+
def frameworks_info
|
216
|
+
return @frameworks if @frameworks
|
217
|
+
info = client_info
|
218
|
+
@frameworks = []
|
219
|
+
if info[:frameworks]
|
220
|
+
info[:frameworks].each_value { |f| @frameworks << [f[:name]] }
|
221
|
+
end
|
222
|
+
@frameworks
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module VMC::Cli::Command
|
2
|
+
class Manifest < Base
|
3
|
+
include VMC::Cli::ManifestHelper
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
super
|
7
|
+
|
8
|
+
# don't resolve any of the manifest template stuff
|
9
|
+
if manifest_file
|
10
|
+
@manifest = load_manifest_structure manifest_file
|
11
|
+
else
|
12
|
+
@manifest = {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def edit
|
17
|
+
build_manifest
|
18
|
+
save_manifest
|
19
|
+
end
|
20
|
+
|
21
|
+
def extend(which)
|
22
|
+
parent = load_manifest_structure which
|
23
|
+
@manifest = load_manifest_structure which
|
24
|
+
|
25
|
+
build_manifest
|
26
|
+
|
27
|
+
simplify(@manifest, parent)
|
28
|
+
|
29
|
+
@manifest["inherit"] ||= []
|
30
|
+
@manifest["inherit"] << which
|
31
|
+
|
32
|
+
save_manifest(ask("Save where?"))
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def simplify(child, parent)
|
38
|
+
return unless child.is_a?(Hash) and parent.is_a?(Hash)
|
39
|
+
|
40
|
+
child.reject! do |k, v|
|
41
|
+
if v == parent[k]
|
42
|
+
puts "rejecting #{k}"
|
43
|
+
true
|
44
|
+
else
|
45
|
+
simplify(v, parent[k])
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_manifest
|
52
|
+
@application = ask("Configure for which application?", :default => ".")
|
53
|
+
interact true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module VMC::Cli::Command
|
2
|
+
|
3
|
+
class Misc < Base
|
4
|
+
def version
|
5
|
+
say "vmc #{VMC::Cli::VERSION}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def target
|
9
|
+
return display JSON.pretty_generate({:target => target_url}) if @options[:json]
|
10
|
+
banner "[#{target_url}]"
|
11
|
+
end
|
12
|
+
|
13
|
+
def targets
|
14
|
+
targets = VMC::Cli::Config.targets
|
15
|
+
return display JSON.pretty_generate(targets) if @options[:json]
|
16
|
+
return display 'None specified' if targets.empty?
|
17
|
+
targets_table = table do |t|
|
18
|
+
t.headings = 'Target', 'Authorization'
|
19
|
+
targets.each { |target, token| t << [target, token] }
|
20
|
+
end
|
21
|
+
display "\n"
|
22
|
+
display targets_table
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :tokens :targets
|
26
|
+
|
27
|
+
def set_target(target_url)
|
28
|
+
target_url = "http://#{target_url}" unless /^https?/ =~ target_url
|
29
|
+
target_url = target_url.gsub(/\/+$/, '')
|
30
|
+
client = VMC::Client.new(target_url)
|
31
|
+
unless client.target_valid?
|
32
|
+
if prompt_ok
|
33
|
+
display "Host is not available or is not valid: '#{target_url}'".red
|
34
|
+
show_response = ask "Would you like see the response?",
|
35
|
+
:default => false
|
36
|
+
display "\n<<<\n#{client.raw_info}\n>>>\n" if show_response
|
37
|
+
end
|
38
|
+
exit(false)
|
39
|
+
else
|
40
|
+
VMC::Cli::Config.store_target(target_url)
|
41
|
+
say "Successfully targeted to [#{target_url}]".green
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def info
|
46
|
+
info = client_info
|
47
|
+
return display JSON.pretty_generate(info) if @options[:json]
|
48
|
+
|
49
|
+
display "\n#{info[:description]}"
|
50
|
+
display "For support visit #{info[:support]}"
|
51
|
+
display ""
|
52
|
+
display "Target: #{target_url} (v#{info[:version]})"
|
53
|
+
display "Client: v#{VMC::Cli::VERSION}"
|
54
|
+
if info[:user]
|
55
|
+
display ''
|
56
|
+
display "User: #{info[:user]}"
|
57
|
+
end
|
58
|
+
if usage = info[:usage] and limits = info[:limits]
|
59
|
+
tmem = pretty_size(limits[:memory]*1024*1024)
|
60
|
+
mem = pretty_size(usage[:memory]*1024*1024)
|
61
|
+
tser = limits[:services]
|
62
|
+
ser = usage[:services]
|
63
|
+
tapps = limits[:apps] || 0
|
64
|
+
apps = usage[:apps] || 0
|
65
|
+
display "Usage: Memory (#{mem} of #{tmem} total)"
|
66
|
+
display " Services (#{ser} of #{tser} total)"
|
67
|
+
display " Apps (#{apps} of #{tapps} total)" if limits[:apps]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def runtimes
|
72
|
+
raise VMC::Client::AuthError unless client.logged_in?
|
73
|
+
return display JSON.pretty_generate(runtimes_info) if @options[:json]
|
74
|
+
return display "No Runtimes" if runtimes_info.empty?
|
75
|
+
rtable = table do |t|
|
76
|
+
t.headings = 'Name', 'Description', 'Version'
|
77
|
+
runtimes_info.each_value { |rt| t << [rt[:name], rt[:description], rt[:version]] }
|
78
|
+
end
|
79
|
+
display "\n"
|
80
|
+
display rtable
|
81
|
+
end
|
82
|
+
|
83
|
+
def frameworks
|
84
|
+
raise VMC::Client::AuthError unless client.logged_in?
|
85
|
+
return display JSON.pretty_generate(frameworks_info) if @options[:json]
|
86
|
+
return display "No Frameworks" if frameworks_info.empty?
|
87
|
+
rtable = table do |t|
|
88
|
+
t.headings = ['Name']
|
89
|
+
frameworks_info.each { |f| t << f }
|
90
|
+
end
|
91
|
+
display "\n"
|
92
|
+
display rtable
|
93
|
+
end
|
94
|
+
|
95
|
+
def aliases
|
96
|
+
aliases = VMC::Cli::Config.aliases
|
97
|
+
return display JSON.pretty_generate(aliases) if @options[:json]
|
98
|
+
return display "No Aliases" if aliases.empty?
|
99
|
+
atable = table do |t|
|
100
|
+
t.headings = 'Alias', 'Command'
|
101
|
+
aliases.each { |k,v| t << [k, v] }
|
102
|
+
end
|
103
|
+
display "\n"
|
104
|
+
display atable
|
105
|
+
end
|
106
|
+
|
107
|
+
def alias(k, v=nil)
|
108
|
+
k,v = k.split('=') unless v
|
109
|
+
aliases = VMC::Cli::Config.aliases
|
110
|
+
aliases[k] = v
|
111
|
+
VMC::Cli::Config.store_aliases(aliases)
|
112
|
+
display "Successfully aliased '#{k}' to '#{v}'".green
|
113
|
+
end
|
114
|
+
|
115
|
+
def unalias(key)
|
116
|
+
aliases = VMC::Cli::Config.aliases
|
117
|
+
if aliases.has_key?(key)
|
118
|
+
aliases.delete(key)
|
119
|
+
VMC::Cli::Config.store_aliases(aliases)
|
120
|
+
display "Successfully unaliased '#{key}'".green
|
121
|
+
else
|
122
|
+
display "Unknown alias '#{key}'".red
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "uuidtools"
|
2
|
+
|
3
|
+
module VMC::Cli::Command
|
4
|
+
|
5
|
+
class Services < Base
|
6
|
+
include VMC::Cli::ServicesHelper
|
7
|
+
include VMC::Cli::TunnelHelper
|
8
|
+
|
9
|
+
def services
|
10
|
+
ss = client.services_info
|
11
|
+
ps = client.services
|
12
|
+
ps.sort! {|a, b| a[:name] <=> b[:name] }
|
13
|
+
|
14
|
+
if @options[:json]
|
15
|
+
services = { :system => ss, :provisioned => ps }
|
16
|
+
return display JSON.pretty_generate(services)
|
17
|
+
end
|
18
|
+
display_system_services(ss)
|
19
|
+
display_provisioned_services(ps)
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_service(service=nil, name=nil, appname=nil)
|
23
|
+
unless no_prompt || service
|
24
|
+
services = client.services_info
|
25
|
+
err 'No services available to provision' if services.empty?
|
26
|
+
service = ask(
|
27
|
+
"Which service would you like to provision?",
|
28
|
+
{ :indexed => true,
|
29
|
+
:choices =>
|
30
|
+
services.values.collect { |type|
|
31
|
+
type.keys.collect(&:to_s)
|
32
|
+
}.flatten
|
33
|
+
}
|
34
|
+
)
|
35
|
+
end
|
36
|
+
name = @options[:name] unless name
|
37
|
+
unless name
|
38
|
+
name = random_service_name(service)
|
39
|
+
picked_name = true
|
40
|
+
end
|
41
|
+
create_service_banner(service, name, picked_name)
|
42
|
+
appname = @options[:bind] unless appname
|
43
|
+
bind_service_banner(name, appname) if appname
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_service(service=nil)
|
47
|
+
unless no_prompt || service
|
48
|
+
user_services = client.services
|
49
|
+
err 'No services available to delete' if user_services.empty?
|
50
|
+
service = ask(
|
51
|
+
"Which service would you like to delete?",
|
52
|
+
{ :indexed => true,
|
53
|
+
:choices => user_services.collect { |s| s[:name] }
|
54
|
+
}
|
55
|
+
)
|
56
|
+
end
|
57
|
+
err "Service name required." unless service
|
58
|
+
display "Deleting service [#{service}]: ", false
|
59
|
+
client.delete_service(service)
|
60
|
+
display 'OK'.green
|
61
|
+
end
|
62
|
+
|
63
|
+
def bind_service(service, appname)
|
64
|
+
bind_service_banner(service, appname)
|
65
|
+
end
|
66
|
+
|
67
|
+
def unbind_service(service, appname)
|
68
|
+
unbind_service_banner(service, appname)
|
69
|
+
end
|
70
|
+
|
71
|
+
def clone_services(src_app, dest_app)
|
72
|
+
begin
|
73
|
+
src = client.app_info(src_app)
|
74
|
+
dest = client.app_info(dest_app)
|
75
|
+
rescue
|
76
|
+
end
|
77
|
+
|
78
|
+
err "Application '#{src_app}' does not exist" unless src
|
79
|
+
err "Application '#{dest_app}' does not exist" unless dest
|
80
|
+
|
81
|
+
services = src[:services]
|
82
|
+
err 'No services to clone' unless services && !services.empty?
|
83
|
+
services.each { |service| bind_service_banner(service, dest_app, false) }
|
84
|
+
check_app_for_restart(dest_app)
|
85
|
+
end
|
86
|
+
|
87
|
+
def tunnel(service=nil, client_name=nil)
|
88
|
+
unless defined? Caldecott
|
89
|
+
display "To use `vmc tunnel', you must first install Caldecott:"
|
90
|
+
display ""
|
91
|
+
display "\tgem install caldecott"
|
92
|
+
display ""
|
93
|
+
display "Note that you'll need a C compiler. If you're on OS X, Xcode"
|
94
|
+
display "will provide one. If you're on Windows, try DevKit."
|
95
|
+
display ""
|
96
|
+
display "This manual step will be removed in the future."
|
97
|
+
display ""
|
98
|
+
err "Caldecott is not installed."
|
99
|
+
end
|
100
|
+
|
101
|
+
ps = client.services
|
102
|
+
err "No services available to tunnel to" if ps.empty?
|
103
|
+
|
104
|
+
unless service
|
105
|
+
choices = ps.collect { |s| s[:name] }.sort
|
106
|
+
service = ask(
|
107
|
+
"Which service to tunnel to?",
|
108
|
+
:choices => choices,
|
109
|
+
:indexed => true
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
info = ps.select { |s| s[:name] == service }.first
|
114
|
+
|
115
|
+
err "Unknown service '#{service}'" unless info
|
116
|
+
|
117
|
+
port = pick_tunnel_port(@options[:port] || 10000)
|
118
|
+
|
119
|
+
raise VMC::Client::AuthError unless client.logged_in?
|
120
|
+
|
121
|
+
if not tunnel_pushed?
|
122
|
+
display "Deploying tunnel application '#{tunnel_appname}'."
|
123
|
+
auth = UUIDTools::UUID.random_create.to_s
|
124
|
+
push_caldecott(auth)
|
125
|
+
bind_service_banner(service, tunnel_appname, false)
|
126
|
+
start_caldecott
|
127
|
+
else
|
128
|
+
auth = tunnel_auth
|
129
|
+
end
|
130
|
+
|
131
|
+
if not tunnel_healthy?(auth)
|
132
|
+
display "Redeploying tunnel application '#{tunnel_appname}'."
|
133
|
+
|
134
|
+
# We don't expect caldecott not to be running, so take the
|
135
|
+
# most aggressive restart method.. delete/re-push
|
136
|
+
client.delete_app(tunnel_appname)
|
137
|
+
invalidate_tunnel_app_info
|
138
|
+
|
139
|
+
push_caldecott(auth)
|
140
|
+
bind_service_banner(service, tunnel_appname, false)
|
141
|
+
start_caldecott
|
142
|
+
end
|
143
|
+
|
144
|
+
if not tunnel_bound?(service)
|
145
|
+
bind_service_banner(service, tunnel_appname)
|
146
|
+
end
|
147
|
+
|
148
|
+
conn_info = tunnel_connection_info info[:vendor], service, auth
|
149
|
+
display_tunnel_connection_info(conn_info)
|
150
|
+
display "Starting tunnel to #{service.bold} on port #{port.to_s.bold}."
|
151
|
+
start_tunnel(port, conn_info, auth)
|
152
|
+
|
153
|
+
clients = get_clients_for(info[:vendor])
|
154
|
+
|
155
|
+
if clients.empty?
|
156
|
+
client_name ||= "none"
|
157
|
+
else
|
158
|
+
client_name ||= ask(
|
159
|
+
"Which client would you like to start?",
|
160
|
+
:choices => ["none"] + clients.keys,
|
161
|
+
:indexed => true
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
if client_name == "none"
|
166
|
+
wait_for_tunnel_end
|
167
|
+
else
|
168
|
+
wait_for_tunnel_start(port)
|
169
|
+
unless start_local_prog(clients, client_name, conn_info, port)
|
170
|
+
err "'#{client_name}' executation failed; is it in your $PATH?"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_clients_for(type)
|
176
|
+
conf = VMC::Cli::Config.clients
|
177
|
+
conf[type] || {}
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module VMC::Cli::Command
|
2
|
+
|
3
|
+
class User < Base
|
4
|
+
|
5
|
+
def info
|
6
|
+
info = client_info
|
7
|
+
username = info[:user] || 'N/A'
|
8
|
+
return display JSON.pretty_generate([username]) if @options[:json]
|
9
|
+
display "\n[#{username}]"
|
10
|
+
end
|
11
|
+
|
12
|
+
def login(email=nil)
|
13
|
+
email = @options[:email] unless email
|
14
|
+
password = @options[:password]
|
15
|
+
tries ||= 0
|
16
|
+
|
17
|
+
unless no_prompt
|
18
|
+
display "Attempting login to [#{target_url}]" if target_url
|
19
|
+
email ||= ask("Email")
|
20
|
+
password ||= ask("Password", :echo => "*")
|
21
|
+
end
|
22
|
+
|
23
|
+
err "Need a valid email" unless email
|
24
|
+
err "Need a password" unless password
|
25
|
+
login_and_save_token(email, password)
|
26
|
+
say "Successfully logged into [#{target_url}]".green
|
27
|
+
rescue VMC::Client::TargetError
|
28
|
+
display "Problem with login, invalid account or password when attempting to login to '#{target_url}'".red
|
29
|
+
retry if (tries += 1) < 3 && prompt_ok && !@options[:password]
|
30
|
+
exit 1
|
31
|
+
rescue => e
|
32
|
+
display "Problem with login to '#{target_url}', #{e}, try again or register for an account.".red
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def logout
|
37
|
+
VMC::Cli::Config.remove_token_file
|
38
|
+
say "Successfully logged out of [#{target_url}]".green
|
39
|
+
end
|
40
|
+
|
41
|
+
def change_password(password=nil)
|
42
|
+
info = client_info
|
43
|
+
email = info[:user]
|
44
|
+
err "Need to be logged in to change password." unless email
|
45
|
+
say "Changing password for '#{email}'\n"
|
46
|
+
unless no_prompt
|
47
|
+
password = ask "New Password", :echo => "*"
|
48
|
+
password2 = ask "Verify Password", :echo => "*"
|
49
|
+
err "Passwords did not match, try again" if password != password2
|
50
|
+
end
|
51
|
+
err "Password required" unless password
|
52
|
+
client.change_password(password)
|
53
|
+
say "\nSuccessfully changed password".green
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def login_and_save_token(email, password)
|
59
|
+
token = client.login(email, password)
|
60
|
+
VMC::Cli::Config.store_token(token, @options[:token_file])
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|