sys 0.1

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 (44) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +103 -0
  3. data/Rakefile +101 -0
  4. data/bin/sys +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 +48 -0
  13. data/lib/cli/commands/admin.rb +80 -0
  14. data/lib/cli/commands/apps.rb +1208 -0
  15. data/lib/cli/commands/base.rb +233 -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 +140 -0
  19. data/lib/cli/commands/services.rb +217 -0
  20. data/lib/cli/commands/user.rb +65 -0
  21. data/lib/cli/config.rb +170 -0
  22. data/lib/cli/console_helper.rb +163 -0
  23. data/lib/cli/core_ext.rb +122 -0
  24. data/lib/cli/errors.rb +19 -0
  25. data/lib/cli/file_helper.rb +123 -0
  26. data/lib/cli/frameworks.rb +265 -0
  27. data/lib/cli/manifest_helper.rb +316 -0
  28. data/lib/cli/runner.rb +568 -0
  29. data/lib/cli/services_helper.rb +104 -0
  30. data/lib/cli/tunnel_helper.rb +336 -0
  31. data/lib/cli/usage.rb +125 -0
  32. data/lib/cli/version.rb +7 -0
  33. data/lib/cli/zip_util.rb +77 -0
  34. data/lib/vmc.rb +3 -0
  35. data/lib/vmc/client.rb +558 -0
  36. data/lib/vmc/const.rb +27 -0
  37. data/lib/vmc/micro.rb +56 -0
  38. data/lib/vmc/micro/switcher/base.rb +97 -0
  39. data/lib/vmc/micro/switcher/darwin.rb +19 -0
  40. data/lib/vmc/micro/switcher/dummy.rb +15 -0
  41. data/lib/vmc/micro/switcher/linux.rb +16 -0
  42. data/lib/vmc/micro/switcher/windows.rb +31 -0
  43. data/lib/vmc/micro/vmrun.rb +158 -0
  44. metadata +217 -0
@@ -0,0 +1,217 @@
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
+
24
+ unless no_prompt || service
25
+ services = client.services_info
26
+ err 'No services available to provision' if services.empty?
27
+ service = ask(
28
+ "Which service would you like to provision?",
29
+ { :indexed => true,
30
+ :choices =>
31
+ services.values.collect { |type|
32
+ type.keys.collect(&:to_s)
33
+ }.flatten
34
+ }
35
+ )
36
+ end
37
+ name = @options[:name] unless name
38
+ unless name
39
+ name = random_service_name(service)
40
+ picked_name = true
41
+ end
42
+
43
+ if client.infra_supported?
44
+ unless no_prompt || @options[:infra]
45
+ @options[:infra] = client.infra_name_for_description(
46
+ ask("Select Infrastructure",
47
+ :indexed => true, :choices => client.infra_descriptions))
48
+ end
49
+ end
50
+
51
+ create_service_banner(service, name, picked_name, @options[:infra])
52
+ appname = @options[:bind] unless appname
53
+ bind_service_banner(name, appname) if appname
54
+ end
55
+
56
+ def delete_service(service=nil)
57
+ unless no_prompt || service
58
+ user_services = client.services
59
+ err 'No services available to delete' if user_services.empty?
60
+ service = ask(
61
+ "Which service would you like to delete?",
62
+ { :indexed => true,
63
+ :choices => user_services.collect { |s| s[:name] }
64
+ }
65
+ )
66
+ end
67
+ err "Service name required." unless service
68
+ display "Deleting service [#{service}]: ", false
69
+ client.delete_service(service)
70
+ display 'OK'.green
71
+ end
72
+
73
+ def bind_service(service, appname)
74
+ bind_service_banner(service, appname)
75
+ end
76
+
77
+ def unbind_service(service, appname)
78
+ unbind_service_banner(service, appname)
79
+ end
80
+
81
+ def clone_services(src_app, dest_app)
82
+ begin
83
+ src = client.app_info(src_app)
84
+ dest = client.app_info(dest_app)
85
+ rescue
86
+ end
87
+
88
+ err "Application '#{src_app}' does not exist" unless src
89
+ err "Application '#{dest_app}' does not exist" unless dest
90
+
91
+ services = src[:services]
92
+ err 'No services to clone' unless services && !services.empty?
93
+ services.each { |service| bind_service_banner(service, dest_app, false) }
94
+ check_app_for_restart(dest_app)
95
+ end
96
+
97
+ def export_service(service)
98
+ display "Exporting data from '#{service}': ", false
99
+ export_info = client.export_service(service)
100
+ if export_info
101
+ display 'OK'.green
102
+ puts export_info[:uri]
103
+ else
104
+ err "Export data from '#{service}': failed"
105
+ end
106
+ end
107
+
108
+ def import_service(service,url)
109
+ display "Importing data into '#{service}': ", false
110
+ import_info = client.import_service(service,url)
111
+ if import_info
112
+ display 'OK'.green
113
+ else
114
+ err "Import data into '#{service}' failed"
115
+ end
116
+ end
117
+
118
+ def tunnel(service=nil, client_name=nil)
119
+ unless defined? Caldecott
120
+ display "To use `sys tunnel', you must first install Caldecott:"
121
+ display ""
122
+ display "\tgem install caldecott"
123
+ display ""
124
+ display "Note that you'll need a C compiler. If you're on OS X, Xcode"
125
+ display "will provide one. If you're on Windows, try DevKit."
126
+ display ""
127
+ display "This manual step will be removed in the future."
128
+ display ""
129
+ err "Caldecott is not installed."
130
+ end
131
+
132
+ ps = client.services
133
+ err "No services available to tunnel to" if ps.empty?
134
+
135
+ unless service
136
+ choices = ps.collect { |s| s[:name] }.sort
137
+ service = ask(
138
+ "Which service to tunnel to?",
139
+ :choices => choices,
140
+ :indexed => true
141
+ )
142
+ end
143
+
144
+ info = ps.select { |s| s[:name] == service }.first
145
+
146
+ err "Unknown service '#{service}'" unless info
147
+
148
+ port = pick_tunnel_port(@options[:port] || 10000)
149
+
150
+ raise VMC::Client::AuthError unless client.logged_in?
151
+
152
+ infra_name = nil
153
+ if client.infra_supported?
154
+ infra_name = info[:infra] ? info[:infra][:name] : default_infra
155
+ err "Infra '#{infra_name}' is not valid" unless client.infra_valid?(infra_name)
156
+ end
157
+
158
+ if not tunnel_pushed?(infra_name)
159
+ display "Deploying tunnel application '#{tunnel_appname(infra_name)}'."
160
+ auth = UUIDTools::UUID.random_create.to_s
161
+ push_caldecott(auth,infra_name)
162
+ bind_service_banner(service, tunnel_appname(infra_name), false)
163
+ start_caldecott(infra_name)
164
+ else
165
+ auth = tunnel_auth(infra_name)
166
+ end
167
+
168
+ if not tunnel_healthy?(auth,infra_name)
169
+ display "Redeploying tunnel application '#{tunnel_appname(infra_name)}'."
170
+
171
+ # We don't expect caldecott not to be running, so take the
172
+ # most aggressive restart method.. delete/re-push
173
+ client.delete_app(tunnel_appname(infra_name))
174
+ invalidate_tunnel_app_info(infra_name)
175
+
176
+ push_caldecott(auth,infra_name)
177
+ bind_service_banner(service, tunnel_appname(infra_name), false)
178
+ start_caldecott(infra_name)
179
+ end
180
+
181
+ if not tunnel_bound?(service,infra_name)
182
+ bind_service_banner(service, tunnel_appname(infra_name))
183
+ end
184
+
185
+ conn_info = tunnel_connection_info info[:vendor], service, auth, infra_name
186
+ display_tunnel_connection_info(conn_info)
187
+ display "Starting tunnel to #{service.bold} on port #{port.to_s.bold}."
188
+ start_tunnel(port, conn_info, auth, infra_name)
189
+
190
+ clients = get_clients_for(info[:vendor])
191
+
192
+ if clients.empty?
193
+ client_name ||= "none"
194
+ else
195
+ client_name ||= ask(
196
+ "Which client would you like to start?",
197
+ :choices => ["none"] + clients.keys,
198
+ :indexed => true
199
+ )
200
+ end
201
+
202
+ if client_name == "none"
203
+ wait_for_tunnel_end
204
+ else
205
+ wait_for_tunnel_start(port)
206
+ unless start_local_prog(clients, client_name, conn_info, port)
207
+ err "'#{client_name}' execution failed; is it in your $PATH?"
208
+ end
209
+ end
210
+ end
211
+
212
+ def get_clients_for(type)
213
+ conf = VMC::Cli::Config.clients
214
+ conf[type] || {}
215
+ end
216
+ end
217
+ 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
data/lib/cli/config.rb ADDED
@@ -0,0 +1,170 @@
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.appfog.com'
11
+
12
+ TARGET_FILE = '~/.sys_target'
13
+ TOKEN_FILE = '~/.sys_token'
14
+ INSTANCES_FILE = '~/.sys_instances'
15
+ ALIASES_FILE = '~/.sys_aliases'
16
+ CLIENTS_FILE = '~/.sys_clients'
17
+ MICRO_FILE = '~/.sys_micro'
18
+
19
+ STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__)
20
+
21
+ class << self
22
+ attr_accessor :colorize
23
+ attr_accessor :output
24
+ attr_accessor :trace
25
+ attr_accessor :nozip
26
+ attr_accessor :infra
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 = "https://#{@target_url}" unless /^https?/ =~ @target_url
37
+ @target_url = @target_url.gsub(/\/+$/, '')
38
+ @target_url
39
+ end
40
+
41
+ def base_of(url)
42
+ url.sub(/^[^\.]+\./, "")
43
+ end
44
+
45
+ def store_target(target_host)
46
+ target_file = File.expand_path(TARGET_FILE)
47
+ lock_and_write(target_file, target_host)
48
+ end
49
+
50
+ def all_tokens(token_file_path=nil)
51
+ token_file = File.expand_path(token_file_path || TOKEN_FILE)
52
+ return nil unless File.exists? token_file
53
+ contents = lock_and_read(token_file).strip
54
+ JSON.parse(contents)
55
+ end
56
+
57
+ alias :targets :all_tokens
58
+
59
+ def auth_token(token_file_path=nil)
60
+ return @token if @token
61
+ tokens = all_tokens(token_file_path)
62
+ @token = tokens[target_url] if tokens
63
+ end
64
+
65
+ def remove_token_file
66
+ FileUtils.rm_f(File.expand_path(TOKEN_FILE))
67
+ end
68
+
69
+ def store_token(token, token_file_path=nil)
70
+ tokens = all_tokens(token_file_path) || {}
71
+ tokens[target_url] = token
72
+ token_file = File.expand_path(token_file_path || TOKEN_FILE)
73
+ lock_and_write(token_file, tokens.to_json)
74
+ end
75
+
76
+ def instances
77
+ instances_file = File.expand_path(INSTANCES_FILE)
78
+ return nil unless File.exists? instances_file
79
+ contents = lock_and_read(instances_file).strip
80
+ JSON.parse(contents)
81
+ end
82
+
83
+ def store_instances(instances)
84
+ instances_file = File.expand_path(INSTANCES_FILE)
85
+ lock_and_write(instances_file, instances.to_json)
86
+ end
87
+
88
+ def aliases
89
+ aliases_file = File.expand_path(ALIASES_FILE)
90
+ # bacward compatible
91
+ unless File.exists? aliases_file
92
+ old_aliases_file = File.expand_path('~/.vmc-aliases')
93
+ FileUtils.mv(old_aliases_file, aliases_file) if File.exists? old_aliases_file
94
+ end
95
+ aliases = YAML.load_file(aliases_file) rescue {}
96
+ end
97
+
98
+ def store_aliases(aliases)
99
+ aliases_file = File.expand_path(ALIASES_FILE)
100
+ File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
101
+ end
102
+
103
+ def micro
104
+ micro_file = File.expand_path(MICRO_FILE)
105
+ return {} unless File.exists? micro_file
106
+ contents = lock_and_read(micro_file).strip
107
+ JSON.parse(contents)
108
+ end
109
+
110
+ def store_micro(micro)
111
+ micro_file = File.expand_path(MICRO_FILE)
112
+ lock_and_write(micro_file, micro.to_json)
113
+ end
114
+
115
+ def deep_merge(a, b)
116
+ merge = proc do |_, old, new|
117
+ if new.is_a?(Hash) and old.is_a?(Hash)
118
+ old.merge(new, &merge)
119
+ else
120
+ new
121
+ end
122
+ end
123
+
124
+ a.merge(b, &merge)
125
+ end
126
+
127
+ def clients
128
+ return @clients if @clients
129
+
130
+ stock = YAML.load_file(STOCK_CLIENTS)
131
+ clients = File.expand_path CLIENTS_FILE
132
+ if File.exists? clients
133
+ user = YAML.load_file(clients)
134
+ @clients = deep_merge(stock, user)
135
+ else
136
+ @clients = stock
137
+ end
138
+ end
139
+
140
+ def lock_and_read(file)
141
+ File.open(file, File::RDONLY) {|f|
142
+ if defined? JRUBY_VERSION
143
+ f.flock(File::LOCK_SH)
144
+ else
145
+ f.flock(File::LOCK_EX)
146
+ end
147
+ contents = f.read
148
+ f.flock(File::LOCK_UN)
149
+ contents
150
+ }
151
+ end
152
+
153
+ def lock_and_write(file, contents)
154
+ File.open(file, File::RDWR | File::CREAT, 0600) {|f|
155
+ f.flock(File::LOCK_EX)
156
+ f.rewind
157
+ f.puts contents
158
+ f.flush
159
+ f.truncate(f.pos)
160
+ f.flock(File::LOCK_UN)
161
+ }
162
+ end
163
+ end
164
+
165
+ def initialize(work_dir = Dir.pwd)
166
+ @work_dir = work_dir
167
+ end
168
+
169
+ end
170
+ end