cloulu 0.2.1 → 0.2.3

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.
@@ -29,13 +29,24 @@ class Mothership
29
29
  input :command, :argument => :optional
30
30
  input :all, :type => :boolean
31
31
  def help
32
+ platform_is_cross = (Object::RUBY_PLATFORM =~ /darwin/i) ? true : false
33
+ platform_is_windows = !platform_is_cross
34
+
35
+ if platform_is_windows
36
+ prefix = ""
37
+ suffix = ""
38
+ else
39
+ prefix = "\x1B[1m\x1B[33m"
40
+ suffix = "\x1B[00m"
41
+ end
42
+
32
43
  # custom header
33
- puts "\x1B[1m\x1B[33m===========================================================\x1B[00m"
34
- puts "\x1B[1m\x1B[33m* Welcome Cloulu Platform\x1B[00m"
35
- puts "\x1B[1m\x1B[33m*\x1B[00m"
36
- puts "\x1B[1m\x1B[33m* @author clouluteam <cloulu@sk.com>\x1B[00m"
37
- puts "\x1B[1m\x1B[33m* @homepage https://cloulu.com, http://blog.cloulu.com\x1B[00m"
38
- puts "\x1B[1m\x1B[33m============================================================\x1B[00m"
44
+ puts "#{prefix}===========================================================#{suffix}"
45
+ puts "#{prefix}* Welcome Cloulu PaaS Platform#{suffix}"
46
+ puts "#{prefix}*#{suffix}"
47
+ puts "#{prefix}* @author clouluteam <cloulu@sk.com>#{suffix}"
48
+ puts "#{prefix}* @homepage https://cloulu.com, https://github.com/cloulu#{suffix}"
49
+ puts "#{prefix}============================================================#{suffix}"
39
50
  puts
40
51
 
41
52
  if name = input[:command]
@@ -23,16 +23,24 @@ module Mothership::Help
23
23
 
24
24
  i = " " * indent
25
25
 
26
+ platform_is_cross = (Object::RUBY_PLATFORM =~ /darwin/i) ? true : false
27
+ platform_is_windows = !platform_is_cross
28
+
26
29
  # set prefix
27
30
  prefix = "*"
28
31
  prefix = ">>" if indent == 1
29
32
 
30
33
  # set color
31
- color = "31"
32
- color = "32" if indent == 1
33
-
34
34
  print i
35
- puts "\x1B[1m\x1B[#{color}m" + prefix + " " + group[:description] + "\x1B[00m"
35
+ if platform_is_windows
36
+ puts prefix + " " + group[:description]
37
+ else
38
+ color = "31"
39
+ color = "32" if indent == 1
40
+
41
+ puts "\x1B[1m\x1B[#{color}m" + prefix + " " + group[:description] + "\x1B[00m"
42
+ end
43
+
36
44
 
37
45
  commands = unique_commands(commands)
38
46
 
@@ -60,78 +60,5 @@ module VMC::App
60
60
  rescue CFoundry::FileError => e
61
61
  fail e.description
62
62
  end
63
-
64
- desc "Stream an app's file contents"
65
- group :apps, :info
66
- input :app, :desc => "Application to inspect the files of",
67
- :argument => true, :from_given => by_name(:app)
68
- input :path, :desc => "Path of file to stream", :argument => :optional
69
- def tail
70
- app = input[:app]
71
-
72
- lines = Queue.new
73
- max_len = 0
74
-
75
- if path = input[:path]
76
- max_len = path.size
77
- app.instances.each do |i|
78
- Thread.new do
79
- stream_path(lines, i, path.split("/"))
80
- end
81
- end
82
- else
83
- app.instances.each do |i|
84
- i.files("logs").each do |path|
85
- len = path.join("/").size
86
- max_len = len if len > max_len
87
-
88
- Thread.new do
89
- stream_path(lines, i, path)
90
- end
91
- end
92
- end
93
- end
94
-
95
- while line = lines.pop
96
- instance, path, log = line
97
-
98
- unless log.end_with?("\n")
99
- log += i("%") if color?
100
- log += "\n"
101
- end
102
-
103
- print "\##{c(instance.id, :instance)} "
104
- print "#{c(path.join("/").ljust(max_len), :name)} "
105
- print log
106
- end
107
- rescue CFoundry::NotFound
108
- fail "Invalid path #{b(path)} for app #{b(app.name)}"
109
- rescue CFoundry::FileError => e
110
- fail e.description
111
- end
112
-
113
- def stream_path(lines, instance, path)
114
- if verbose?
115
- lines << [instance, path, c("streaming...", :good) + "\n"]
116
- end
117
-
118
- instance.stream_file(*path) do |contents|
119
- contents.each_line do |line|
120
- lines << [instance, path, line]
121
- end
122
- end
123
-
124
- lines << [instance, path, c("end of file", :bad) + "\n"]
125
- rescue Timeout::Error
126
- if verbose?
127
- lines << [
128
- instance,
129
- path,
130
- c("timed out; reconnecting...", :bad) + "\n"
131
- ]
132
- end
133
-
134
- retry
135
- end
136
63
  end
137
64
  end
@@ -10,71 +10,20 @@ module VMC::Route
10
10
  group :apps, :info
11
11
  input :app, :desc => "Application to add the URL to",
12
12
  :argument => :optional, :from_given => by_name(:app)
13
- input :host, :desc => "Host name for the route",
14
- :argument => :optional, :default => ""
15
13
  input :domain, :desc => "Domain to add the route to",
16
- :argument => true,
17
- :from_given => proc { |name, space|
18
- if v2?
19
- space.domain_by_name(name) ||
20
- fail_unknown("domain", name)
21
- else
22
- name
23
- end
24
- }
14
+ :argument => true
25
15
  def map
26
16
  app = input[:app]
27
- space = app.space if v2?
17
+ domain = input[:domain]
28
18
 
29
- host = input[:host]
30
- domain = input[:domain, space]
31
-
32
- if v2?
33
- route = find_or_create_route(domain, host, space)
34
- bind_route(route, app) if app
35
- else
36
- with_progress("Updating #{c(app.name, :name)}") do
37
- app.urls << domain
38
- app.update!
39
- end
19
+ with_progress("Updating #{c(app.name, :name)}") do
20
+ app.urls << domain
21
+ app.update!
40
22
  end
41
23
  end
42
24
 
43
25
  private
44
26
 
45
- def bind_route(route, app)
46
- with_progress("Binding #{c(route.name, :name)} to #{c(app.name, :name)}") do
47
- app.add_route(route)
48
- end
49
- end
50
-
51
- def find_or_create_route(domain, host, space)
52
- find_route(domain, host) || create_route(domain, host, space)
53
- end
54
-
55
- def find_route(domain, host)
56
- client.routes_by_host(host, :depth => 0).find { |r| r.domain == domain }
57
- end
58
-
59
- def create_route(domain, host, space)
60
- route = client.route
61
- route.host = host
62
- route.domain = domain
63
- route.space = space
64
-
65
- with_progress("Creating route #{c(route.name, :name)}") do
66
- route.create!
67
- end
68
-
69
- route
70
- end
71
-
72
- def find_domain(space, name)
73
- domain = space.domain_by_name(name, :depth => 0)
74
- fail "Invalid domain '#{name}'" unless domain
75
- domain
76
- end
77
-
78
27
  def ask_app
79
28
  ask("Which application?", :choices => client.apps, :display => proc(&:name))
80
29
  end
data/lib/vmc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module VMC
2
- VERSION = "0.2.1".freeze
2
+ VERSION = "0.2.3".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloulu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -108,23 +108,7 @@ dependencies:
108
108
  - - ~>
109
109
  - !ruby/object:Gem::Version
110
110
  version: 0.2.2
111
- - !ruby/object:Gem::Dependency
112
- name: rr
113
- requirement: !ruby/object:Gem::Requirement
114
- none: false
115
- requirements:
116
- - - ~>
117
- - !ruby/object:Gem::Version
118
- version: '1.0'
119
- type: :development
120
- prerelease: false
121
- version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
- requirements:
124
- - - ~>
125
- - !ruby/object:Gem::Version
126
- version: '1.0'
127
- description: test
111
+ description: cloulu client command line tool
128
112
  email:
129
113
  - cloulu@sk.com
130
114
  executables:
@@ -209,9 +193,6 @@ files:
209
193
  - lib/mothership/parser.rb
210
194
  - lib/mothership/version.rb
211
195
  - lib/mothership.rb
212
- - lib/tunnel-vmc-plugin/plugin.rb
213
- - lib/tunnel-vmc-plugin/tunnel.rb
214
- - lib/tunnel-vmc-plugin/version.rb
215
196
  - lib/uaa/http.rb
216
197
  - lib/uaa/misc.rb
217
198
  - lib/uaa/scim.rb
@@ -1,178 +0,0 @@
1
- require "vmc/cli"
2
- require "tunnel-vmc-plugin/tunnel"
3
-
4
- module VMCTunnel
5
- class Tunnel < VMC::CLI
6
- CLIENTS_FILE = "#{VMC::CONFIG_DIR}/tunnel-clients.yml"
7
- STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__)
8
-
9
- desc "Create a local tunnel to a service."
10
- group :services, :manage
11
- input(:instance, :argument => :optional,
12
- :from_given => find_by_name("service instance"),
13
- :desc => "Service instance to tunnel to") { |instances|
14
- ask("Which service instance?", :choices => instances,
15
- :display => proc(&:name))
16
- }
17
- input(:client, :argument => :optional,
18
- :desc => "Client to automatically launch") { |clients|
19
- if clients.empty?
20
- "none"
21
- else
22
- ask("Which client would you like to start?",
23
- :choices => clients.keys.unshift("none"))
24
- end
25
- }
26
- input(:port, :default => 10000, :desc => "Port to bind the tunnel to")
27
- def tunnel
28
- instances = client.service_instances
29
- fail "No services available for tunneling." if instances.empty?
30
-
31
- instance = input[:instance, instances.sort_by(&:name)]
32
- vendor = v2? ? instance.service_plan.service.label : instance.vendor
33
- clients = tunnel_clients[vendor] || {}
34
- client_name = input[:client, clients]
35
-
36
- tunnel = CFTunnel.new(client, instance)
37
- port = tunnel.pick_port!(input[:port])
38
-
39
- conn_info =
40
- with_progress("Opening tunnel on port #{c(port, :name)}") do
41
- tunnel.open!
42
- end
43
-
44
- if client_name == "none"
45
- unless quiet?
46
- line
47
- display_tunnel_connection_info(conn_info)
48
-
49
- line
50
- line "Open another shell to run command-line clients or"
51
- line "use a UI tool to connect using the displayed information."
52
- line "Press Ctrl-C to exit..."
53
- end
54
-
55
- tunnel.wait_for_end
56
- else
57
- with_progress("Waiting for local tunnel to become available") do
58
- tunnel.wait_for_start
59
- end
60
-
61
- unless start_local_prog(clients, client_name, conn_info, port)
62
- fail "'#{client_name}' execution failed; is it in your $PATH?"
63
- end
64
- end
65
- end
66
-
67
- def tunnel_clients
68
- return @tunnel_clients if @tunnel_clients
69
- stock_config = YAML.load_file(STOCK_CLIENTS)
70
- custom_config_file = File.expand_path(CLIENTS_FILE)
71
- if File.exists?(custom_config_file)
72
- custom_config = YAML.load_file(custom_config_file)
73
- @tunnel_clients = deep_merge(stock_config, custom_config)
74
- else
75
- @tunnel_clients = stock_config
76
- end
77
- end
78
-
79
- private
80
-
81
- def display_tunnel_connection_info(info)
82
- line "Service connection info:"
83
-
84
- to_show = [nil, nil, nil] # reserved for user, pass, db name
85
- info.keys.each do |k|
86
- case k
87
- when "host", "hostname", "port", "node_id"
88
- # skip
89
- when "user", "username"
90
- # prefer "username" over "user"
91
- to_show[0] = k unless to_show[0] == "username"
92
- when "password"
93
- to_show[1] = k
94
- when "name"
95
- to_show[2] = k
96
- else
97
- to_show << k
98
- end
99
- end
100
- to_show.compact!
101
-
102
- align_len = to_show.collect(&:size).max + 1
103
-
104
- indented do
105
- to_show.each do |k|
106
- # TODO: modify the server services rest call to have explicit knowledge
107
- # about the items to return. It should return all of them if
108
- # the service is unknown so that we don't have to do this weird
109
- # filtering.
110
- line "#{k.ljust align_len}: #{b(info[k])}"
111
- end
112
- end
113
-
114
- line
115
- end
116
-
117
- def start_local_prog(clients, command, info, port)
118
- client = clients[File.basename(command)]
119
-
120
- cmdline = "#{command} "
121
-
122
- case client
123
- when Hash
124
- cmdline << resolve_symbols(client["command"], info, port)
125
- client["environment"].each do |e|
126
- if e =~ /([^=]+)=(["']?)([^"']*)\2/
127
- ENV[$1] = resolve_symbols($3, info, port)
128
- else
129
- fail "Invalid environment variable: #{e}"
130
- end
131
- end
132
- when String
133
- cmdline << resolve_symbols(client, info, port)
134
- else
135
- raise "Unknown client info: #{client.inspect}."
136
- end
137
-
138
- if verbose?
139
- line
140
- line "Launching '#{cmdline}'"
141
- end
142
-
143
- system(cmdline)
144
- end
145
-
146
- def resolve_symbols(str, info, local_port)
147
- str.gsub(/\$\{\s*([^\}]+)\s*\}/) do
148
- sym = $1
149
-
150
- case sym
151
- when "host"
152
- # TODO: determine proper host
153
- "localhost"
154
- when "port"
155
- local_port
156
- when "user", "username"
157
- info["username"]
158
- when /^ask (.+)/
159
- ask($1)
160
- else
161
- info[sym] || raise("Unknown symbol in config: #{sym}")
162
- end
163
- end
164
- end
165
-
166
- def deep_merge(a, b)
167
- merge = proc { |_, old, new|
168
- if old.is_a?(Hash) && new.is_a?(Hash)
169
- old.merge(new, &merge)
170
- else
171
- new
172
- end
173
- }
174
-
175
- a.merge(b, &merge)
176
- end
177
- end
178
- end
@@ -1,308 +0,0 @@
1
- require "addressable/uri"
2
- require "restclient"
3
- require "uuidtools"
4
-
5
- require "caldecott-client"
6
-
7
-
8
- class CFTunnel
9
- HELPER_NAME = "caldecott"
10
- HELPER_APP = File.expand_path("../../../helper-app", __FILE__)
11
-
12
- # bump this AND the version info reported by HELPER_APP/server.rb
13
- # this is to keep the helper in sync with any updates here
14
- HELPER_VERSION = "0.0.4"
15
-
16
- def initialize(client, service, port = 10000)
17
- @client = client
18
- @service = service
19
- @port = port
20
- end
21
-
22
- def open!
23
- if helper
24
- auth = helper_auth
25
-
26
- unless helper_healthy?(auth)
27
- delete_helper
28
- auth = create_helper
29
- end
30
- else
31
- auth = create_helper
32
- end
33
-
34
- bind_to_helper if @service && !helper_already_binds?
35
-
36
- info = get_connection_info(auth)
37
-
38
- start_tunnel(info, auth)
39
-
40
- info
41
- end
42
-
43
- def wait_for_start
44
- 10.times do |n|
45
- begin
46
- TCPSocket.open("localhost", @port).close
47
- return true
48
- rescue => e
49
- sleep 1
50
- end
51
- end
52
-
53
- raise "Could not connect to local tunnel."
54
- end
55
-
56
- def wait_for_end
57
- if @local_tunnel_thread
58
- @local_tunnel_thread.join
59
- else
60
- raise "Tunnel wasn't started!"
61
- end
62
- end
63
-
64
- PORT_RANGE = 10
65
- def pick_port!(port = @port)
66
- original = port
67
-
68
- PORT_RANGE.times do |n|
69
- begin
70
- TCPSocket.open("localhost", port)
71
- port += 1
72
- rescue
73
- return @port = port
74
- end
75
- end
76
-
77
- @port = grab_ephemeral_port
78
- end
79
-
80
- private
81
-
82
- def helper
83
- @helper ||= @client.app_by_name(HELPER_NAME)
84
- end
85
-
86
- def create_helper
87
- auth = UUIDTools::UUID.random_create.to_s
88
- push_helper(auth)
89
- start_helper
90
- auth
91
- end
92
-
93
- def helper_auth
94
- helper.env["CALDECOTT_AUTH"]
95
- end
96
-
97
- def helper_healthy?(token)
98
- return false unless helper.healthy?
99
-
100
- begin
101
- response = RestClient.get(
102
- "#{helper_url}/info",
103
- "Auth-Token" => token
104
- )
105
-
106
- info = JSON.parse(response)
107
- if info["version"] == HELPER_VERSION
108
- true
109
- else
110
- stop_helper
111
- false
112
- end
113
- rescue RestClient::Exception
114
- stop_helper
115
- false
116
- end
117
- end
118
-
119
- def helper_already_binds?
120
- helper.binds? @service
121
- end
122
-
123
- def push_helper(token)
124
- target_base = @client.target.sub(/^[^\.]+\./, "")
125
-
126
- url = "#{random_helper_url}.#{target_base}"
127
- is_v2 = @client.is_a?(CFoundry::V2::Client)
128
-
129
- app = @client.app
130
- app.name = HELPER_NAME
131
- app.framework = @client.framework_by_name("sinatra")
132
- app.runtime = @client.runtime_by_name("ruby19")
133
- app.command = "bundle exec ruby server.rb -p $VCAP_APP_PORT"
134
- app.total_instances = 1
135
- app.memory = 64
136
- app.env = { "CALDECOTT_AUTH" => token }
137
-
138
- if is_v2
139
- app.space = @client.current_space
140
- else
141
- app.services = [@service] if @service
142
- app.url = url
143
- end
144
-
145
- app.create!
146
-
147
- if is_v2
148
- app.bind(@service) if @service
149
- app.create_route(url)
150
- end
151
-
152
- begin
153
- app.upload(HELPER_APP)
154
- invalidate_tunnel_app_info
155
- rescue
156
- app.delete!
157
- raise
158
- end
159
- end
160
-
161
- def delete_helper
162
- helper.delete!
163
- invalidate_tunnel_app_info
164
- end
165
-
166
- def stop_helper
167
- helper.stop!
168
- invalidate_tunnel_app_info
169
- end
170
-
171
- TUNNEL_CHECK_LIMIT = 60
172
- def start_helper
173
- helper.start!
174
-
175
- seconds = 0
176
- until helper.healthy?
177
- sleep 1
178
- seconds += 1
179
- if seconds == TUNNEL_CHECK_LIMIT
180
- raise "Helper application failed to start."
181
- end
182
- end
183
-
184
- invalidate_tunnel_app_info
185
- end
186
-
187
- def bind_to_helper
188
- helper.bind(@service)
189
- helper.restart!
190
- end
191
-
192
- def invalidate_tunnel_app_info
193
- @helper_url = nil
194
- @helper = nil
195
- end
196
-
197
- def helper_url
198
- return @helper_url if @helper_url
199
-
200
- tun_url = helper.url
201
-
202
- ["https", "http"].each do |scheme|
203
- url = "#{scheme}://#{tun_url}"
204
- begin
205
- RestClient.get(url)
206
-
207
- # https failed
208
- rescue Errno::ECONNREFUSED
209
-
210
- # we expect a 404 since this request isn't auth'd
211
- rescue RestClient::ResourceNotFound
212
- return @helper_url = url
213
- end
214
- end
215
-
216
- raise "Cannot determine URL for #{tun_url}"
217
- end
218
-
219
- def get_connection_info(token)
220
- response = nil
221
- 10.times do
222
- begin
223
- response =
224
- RestClient.get(
225
- helper_url + "/" + safe_path("services", @service.name),
226
- "Auth-Token" => token)
227
-
228
- break
229
- rescue RestClient::Exception => e
230
- sleep 1
231
- end
232
- end
233
-
234
- unless response
235
- raise "Remote tunnel helper is unaware of #{@service.name}!"
236
- end
237
-
238
- is_v2 = @client.is_a?(CFoundry::V2::Client)
239
-
240
- info = JSON.parse(response)
241
- case (is_v2 ? @service.service_plan.service.label : @service.vendor)
242
- when "rabbitmq"
243
- uri = Addressable::URI.parse info["url"]
244
- info["hostname"] = uri.host
245
- info["port"] = uri.port
246
- info["vhost"] = uri.path[1..-1]
247
- info["user"] = uri.user
248
- info["password"] = uri.password
249
- info.delete "url"
250
-
251
- # we use "db" as the "name" for mongo
252
- # existing "name" is junk
253
- when "mongodb"
254
- info["name"] = info["db"]
255
- info.delete "db"
256
-
257
- # our "name" is irrelevant for redis
258
- when "redis"
259
- info.delete "name"
260
-
261
- when "filesystem"
262
- raise "Tunneling is not supported for this type of service"
263
- end
264
-
265
- ["hostname", "port", "password"].each do |k|
266
- raise "Could not determine #{k} for #{@service.name}" if info[k].nil?
267
- end
268
-
269
- info
270
- end
271
-
272
- def start_tunnel(conn_info, auth)
273
- @local_tunnel_thread = Thread.new do
274
- Caldecott::Client.start({
275
- :local_port => @port,
276
- :tun_url => helper_url,
277
- :dst_host => conn_info["hostname"],
278
- :dst_port => conn_info["port"],
279
- :log_file => STDOUT,
280
- :log_level => ENV["VMC_TUNNEL_DEBUG"] || "ERROR",
281
- :auth_token => auth,
282
- :quiet => true
283
- })
284
- end
285
-
286
- at_exit { @local_tunnel_thread.kill }
287
- end
288
-
289
- def random_helper_url
290
- random = sprintf("%x", rand(1000000))
291
- "caldecott-#{random}"
292
- end
293
-
294
- def safe_path(*segments)
295
- segments.flatten.collect { |x|
296
- URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
297
- }.join("/")
298
- end
299
-
300
- def grab_ephemeral_port
301
- socket = TCPServer.new("0.0.0.0", 0)
302
- socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
303
- Socket.do_not_reverse_lookup = true
304
- socket.addr[1]
305
- ensure
306
- socket.close
307
- end
308
- end
@@ -1,3 +0,0 @@
1
- module VMCTunnel
2
- VERSION = "0.2.2".freeze
3
- end