vmcu 0.3.17

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.
Files changed (43) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +160 -0
  3. data/Rakefile +101 -0
  4. data/bin/vmcu +6 -0
  5. data/caldecott_helper/Gemfile +10 -0
  6. data/caldecott_helper/Gemfile.lock +48 -0
  7. data/caldecott_helper/server.rb +43 -0
  8. data/config/clients.yml +17 -0
  9. data/config/micro/offline.conf +2 -0
  10. data/config/micro/paths.yml +22 -0
  11. data/config/micro/refresh_ip.rb +20 -0
  12. data/lib/cli.rb +47 -0
  13. data/lib/cli/commands/admin.rb +80 -0
  14. data/lib/cli/commands/apps.rb +1128 -0
  15. data/lib/cli/commands/base.rb +238 -0
  16. data/lib/cli/commands/manifest.rb +56 -0
  17. data/lib/cli/commands/micro.rb +115 -0
  18. data/lib/cli/commands/misc.rb +277 -0
  19. data/lib/cli/commands/services.rb +180 -0
  20. data/lib/cli/commands/user.rb +96 -0
  21. data/lib/cli/config.rb +192 -0
  22. data/lib/cli/console_helper.rb +157 -0
  23. data/lib/cli/core_ext.rb +122 -0
  24. data/lib/cli/errors.rb +19 -0
  25. data/lib/cli/frameworks.rb +244 -0
  26. data/lib/cli/manifest_helper.rb +302 -0
  27. data/lib/cli/runner.rb +543 -0
  28. data/lib/cli/services_helper.rb +84 -0
  29. data/lib/cli/tunnel_helper.rb +332 -0
  30. data/lib/cli/usage.rb +118 -0
  31. data/lib/cli/version.rb +7 -0
  32. data/lib/cli/zip_util.rb +77 -0
  33. data/lib/vmc.rb +3 -0
  34. data/lib/vmc/client.rb +591 -0
  35. data/lib/vmc/const.rb +22 -0
  36. data/lib/vmc/micro.rb +56 -0
  37. data/lib/vmc/micro/switcher/base.rb +97 -0
  38. data/lib/vmc/micro/switcher/darwin.rb +19 -0
  39. data/lib/vmc/micro/switcher/dummy.rb +15 -0
  40. data/lib/vmc/micro/switcher/linux.rb +16 -0
  41. data/lib/vmc/micro/switcher/windows.rb +31 -0
  42. data/lib/vmc/micro/vmrun.rb +158 -0
  43. metadata +263 -0
@@ -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 `vmcu 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}' execution 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,96 @@
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
+ if VMC::Cli::Config.uhuru_target
14
+ begin
15
+ token = email
16
+ token = @options[:token] unless token
17
+
18
+ unless no_prompt
19
+ display "Attempting login to Uhuru AppCloud [#{target_url}]" if target_url
20
+ token ||= ask("Token")
21
+ end
22
+
23
+ err "Need a valid authentication token" unless token
24
+ uhuru_login_and_save_token(token)
25
+ say "Successfully logged into Uhuru AppCloud [#{target_url}]".green
26
+ rescue => e
27
+ display "Problem with login to Uhuru AppCloud '#{target_url}', #{e}, try again or get a new one time token or regular authentication token.".red
28
+ exit 1
29
+ end
30
+ else
31
+ begin
32
+ email = @options[:email] unless email
33
+ password = @options[:password]
34
+ tries ||= 0
35
+
36
+ unless no_prompt
37
+ display "Attempting login to [#{target_url}]" if target_url
38
+ email ||= ask("Email")
39
+ password ||= ask("Password", :echo => "*")
40
+ end
41
+
42
+ err "Need a valid email" unless email
43
+ err "Need a password" unless password
44
+ login_and_save_token(email, password)
45
+ say "Successfully logged into [#{target_url}]".green
46
+ rescue VMC::Client::TargetError
47
+ display "Problem with login, invalid account or password when attempting to login to '#{target_url}'".red
48
+ retry if (tries += 1) < 3 && prompt_ok && !@options[:password]
49
+ exit 1
50
+ rescue => e
51
+ display "Problem with login to '#{target_url}', #{e}, try again or register for an account.".red
52
+ exit 1
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ def logout
59
+ VMC::Cli::Config.remove_token_file
60
+ if VMC::Cli::Config.uhuru_target
61
+ VMC::Cli::Config.store_uhuru_target(target_url, {})
62
+ end
63
+ say "Successfully logged out of [#{target_url}]".green
64
+ end
65
+
66
+ def change_password(password=nil)
67
+ info = client_info
68
+ email = info[:user]
69
+ err "Need to be logged in to change password." unless email
70
+ say "Changing password for '#{email}'\n"
71
+ unless no_prompt
72
+ password = ask "New Password", :echo => "*"
73
+ password2 = ask "Verify Password", :echo => "*"
74
+ err "Passwords did not match, try again" if password != password2
75
+ end
76
+ err "Password required" unless password
77
+ client.change_password(password)
78
+ say "\nSuccessfully changed password".green
79
+ end
80
+
81
+ private
82
+
83
+ def uhuru_login_and_save_token(token)
84
+ uhuru_target = client.uhuru_login(token)
85
+ raise "Unable to login" unless uhuru_target
86
+ VMC::Cli::Config.store_uhuru_target(target_url, uhuru_target)
87
+ end
88
+
89
+ def login_and_save_token(email, password)
90
+ token = client.login(email, password)
91
+ VMC::Cli::Config.store_token(token, @options[:token_file])
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,192 @@
1
+ require "yaml"
2
+ require 'fileutils'
3
+
4
+ require 'rubygems'
5
+ require 'json/pure'
6
+
7
+ module VMC::Cli
8
+ class Config
9
+
10
+ DEFAULT_TARGET = 'api.vcap.me'
11
+
12
+ TARGET_FILE = '~/.vmc_target'
13
+ UHURU_TARGET_FILE = '~/.vmc_uhuru_target'
14
+ TOKEN_FILE = '~/.vmc_token'
15
+ INSTANCES_FILE = '~/.vmc_instances'
16
+ ALIASES_FILE = '~/.vmc_aliases'
17
+ CLIENTS_FILE = '~/.vmc_clients'
18
+ MICRO_FILE = '~/.vmc_micro'
19
+
20
+ STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__)
21
+
22
+ class << self
23
+ attr_accessor :colorize
24
+ attr_accessor :output
25
+ attr_accessor :trace
26
+ attr_accessor :nozip
27
+
28
+ def target_url
29
+ return @target_url if @target_url
30
+ target_file = File.expand_path(TARGET_FILE)
31
+ if File.exists? target_file
32
+ @target_url = lock_and_read(target_file).strip
33
+ else
34
+ @target_url = DEFAULT_TARGET
35
+ end
36
+ @target_url = "http://#{@target_url}" unless /^https?/ =~ @target_url
37
+ @target_url = @target_url.gsub(/\/+$/, '')
38
+ @target_url
39
+ end
40
+
41
+ def uhuru_target
42
+ (all_uhuru_targets || {})[target_url.to_sym]
43
+ end
44
+
45
+ def all_uhuru_targets
46
+ uhuru_target_file = File.expand_path(UHURU_TARGET_FILE)
47
+ return nil unless File.exists? uhuru_target_file
48
+ contents = lock_and_read(uhuru_target_file).strip
49
+ JSON.parse(contents, :symbolize_names => true)
50
+ end
51
+
52
+ def base_of(url)
53
+ url.sub(/^[^\.]+\./, "")
54
+ end
55
+
56
+ def suggest_url
57
+ @suggest_url ||= base_of(target_url)
58
+ end
59
+
60
+ def store_target(target_host)
61
+ target_file = File.expand_path(TARGET_FILE)
62
+ lock_and_write(target_file, target_host)
63
+ end
64
+
65
+ def store_uhuru_target(host, uhuru_target)
66
+ uhuru_target_urls = all_uhuru_targets || {}
67
+ uhuru_target_urls[host.to_sym] = uhuru_target
68
+ uhuru_target_file = File.expand_path(UHURU_TARGET_FILE)
69
+ lock_and_write(uhuru_target_file, JSON.pretty_generate(uhuru_target_urls))
70
+ end
71
+
72
+ def all_tokens(token_file_path=nil)
73
+ token_file = File.expand_path(token_file_path || TOKEN_FILE)
74
+ return nil unless File.exists? token_file
75
+ contents = lock_and_read(token_file).strip
76
+ JSON.parse(contents)
77
+ end
78
+
79
+ alias :targets :all_tokens
80
+
81
+ def auth_token(token_file_path=nil)
82
+ return @token if @token
83
+ tokens = all_tokens(token_file_path)
84
+ @token = tokens[target_url] if tokens
85
+ end
86
+
87
+ def remove_token_file
88
+ FileUtils.rm_f(File.expand_path(TOKEN_FILE))
89
+ end
90
+
91
+ def store_token(token, token_file_path=nil)
92
+ tokens = all_tokens(token_file_path) || {}
93
+ tokens[target_url] = token
94
+ token_file = File.expand_path(token_file_path || TOKEN_FILE)
95
+ lock_and_write(token_file, tokens.to_json)
96
+ end
97
+
98
+ def instances
99
+ instances_file = File.expand_path(INSTANCES_FILE)
100
+ return nil unless File.exists? instances_file
101
+ contents = lock_and_read(instances_file).strip
102
+ JSON.parse(contents)
103
+ end
104
+
105
+ def store_instances(instances)
106
+ instances_file = File.expand_path(INSTANCES_FILE)
107
+ lock_and_write(instances_file, instances.to_json)
108
+ end
109
+
110
+ def aliases
111
+ aliases_file = File.expand_path(ALIASES_FILE)
112
+ # bacward compatible
113
+ unless File.exists? aliases_file
114
+ old_aliases_file = File.expand_path('~/.vmc-aliases')
115
+ FileUtils.mv(old_aliases_file, aliases_file) if File.exists? old_aliases_file
116
+ end
117
+ aliases = YAML.load_file(aliases_file) rescue {}
118
+ end
119
+
120
+ def store_aliases(aliases)
121
+ aliases_file = File.expand_path(ALIASES_FILE)
122
+ File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
123
+ end
124
+
125
+ def micro
126
+ micro_file = File.expand_path(MICRO_FILE)
127
+ return {} unless File.exists? micro_file
128
+ contents = lock_and_read(micro_file).strip
129
+ JSON.parse(contents)
130
+ end
131
+
132
+ def store_micro(micro)
133
+ micro_file = File.expand_path(MICRO_FILE)
134
+ lock_and_write(micro_file, micro.to_json)
135
+ end
136
+
137
+ def deep_merge(a, b)
138
+ merge = proc do |_, old, new|
139
+ if new.is_a?(Hash) and old.is_a?(Hash)
140
+ old.merge(new, &merge)
141
+ else
142
+ new
143
+ end
144
+ end
145
+
146
+ a.merge(b, &merge)
147
+ end
148
+
149
+ def clients
150
+ return @clients if @clients
151
+
152
+ stock = YAML.load_file(STOCK_CLIENTS)
153
+ clients = File.expand_path CLIENTS_FILE
154
+ if File.exists? clients
155
+ user = YAML.load_file(clients)
156
+ @clients = deep_merge(stock, user)
157
+ else
158
+ @clients = stock
159
+ end
160
+ end
161
+
162
+ def lock_and_read(file)
163
+ File.open(file, File::RDONLY) {|f|
164
+ if defined? JRUBY_VERSION
165
+ f.flock(File::LOCK_SH)
166
+ else
167
+ f.flock(File::LOCK_EX)
168
+ end
169
+ contents = f.read
170
+ f.flock(File::LOCK_UN)
171
+ contents
172
+ }
173
+ end
174
+
175
+ def lock_and_write(file, contents)
176
+ File.open(file, File::RDWR | File::CREAT, 0600) {|f|
177
+ f.flock(File::LOCK_EX)
178
+ f.rewind
179
+ f.puts contents
180
+ f.flush
181
+ f.truncate(f.pos)
182
+ f.flock(File::LOCK_UN)
183
+ }
184
+ end
185
+ end
186
+
187
+ def initialize(work_dir = Dir.pwd)
188
+ @work_dir = work_dir
189
+ end
190
+
191
+ end
192
+ end