tools-cf-plugin 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ require "cf/cli"
2
+ require "nats/client"
3
+
4
+ module CFTools
5
+ class DEAAds < CF::App::Base
6
+ def precondition; end
7
+
8
+ desc "Show an overview of DEA advertisements over time."
9
+ group :admin
10
+ input :host, :alias => "-h", :default => "127.0.0.1",
11
+ :desc => "NATS server address"
12
+ input :port, :alias => "-P", :default => 4222, :type => :integer,
13
+ :desc => "NATS server port"
14
+ input :user, :alias => "-u", :default => "nats",
15
+ :desc => "NATS server user"
16
+ input :password, :alias => "-p", :default => "nats",
17
+ :desc => "NATS server password"
18
+ def dea_ads
19
+ host = input[:host]
20
+ port = input[:port]
21
+ user = input[:user]
22
+ pass = input[:password]
23
+
24
+ NATS.start(:uri => "nats://#{user}:#{pass}@#{host}:#{port}") do
25
+ NATS.subscribe("dea.advertise") do |msg|
26
+ payload = JSON.parse(msg)
27
+ id = payload["id"]
28
+ prev = advertisements[id]
29
+ advertisements[id] = [payload, prev && prev.first]
30
+ end
31
+
32
+ EM.add_periodic_timer(3) do
33
+ render_table
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def advertisements
41
+ @advertisements ||= {}
42
+ end
43
+
44
+ def render_table
45
+ rows =
46
+ advertisements.sort.collect do |id, (attrs, prev)|
47
+ idx, _ = id.split("-", 2)
48
+
49
+ [ c(idx, :name),
50
+ list(attrs["stacks"]),
51
+ diff(attrs, prev) { |x| x["app_id_to_count"].values.inject(&:+) },
52
+ diff(attrs, prev, :pretty_memory, :pretty_memory_diff) do |x|
53
+ x["available_memory"]
54
+ end
55
+ ]
56
+ end
57
+
58
+ table(["dea", "stacks", "droplets", "available memory"], rows)
59
+ end
60
+
61
+ def diff(curr, prev, pretty = nil, pretty_diff = :signed)
62
+ new = yield curr
63
+ old = yield prev if prev
64
+ diff = new - old if old
65
+
66
+ display = pretty ? send(pretty, new) : new.to_s
67
+ diff_display = pretty_diff ? send(pretty_diff, diff) : diff.to_s if diff
68
+
69
+ if !old || new == old
70
+ display
71
+ else
72
+ "#{display} (#{diff_display})"
73
+ end
74
+ end
75
+
76
+ def signed(num)
77
+ num > 0 ? c("+#{num}", :good) : c(num, :bad)
78
+ end
79
+
80
+ def pretty_memory(mem)
81
+ human = human_mb(mem)
82
+
83
+ if mem < 1024
84
+ c(human, :bad)
85
+ elsif mem < 2048
86
+ c(human, :warning)
87
+ else
88
+ c(human, :good)
89
+ end
90
+ end
91
+
92
+ def pretty_memory_diff(diff)
93
+ human = human_mb(diff)
94
+
95
+ if diff < 0
96
+ c(human, :bad)
97
+ else
98
+ c("+#{human}", :good)
99
+ end
100
+ end
101
+
102
+ def human_mb(mem)
103
+ human_size(mem * 1024 * 1024)
104
+ end
105
+
106
+ def human_size(num, precision = 1)
107
+ abs = num.abs
108
+
109
+ sizes = %w(T G M K)
110
+ sizes.each.with_index do |suf, i|
111
+ pow = sizes.size - i
112
+ unit = 1024.0 ** pow
113
+ if abs >= unit
114
+ return format("%.#{precision}f%s", num / unit, suf)
115
+ end
116
+ end
117
+
118
+ format("%.#{precision}fB", num)
119
+ end
120
+
121
+ def list(vals)
122
+ if vals.empty?
123
+ d("none")
124
+ else
125
+ vals.join(", ")
126
+ end
127
+ end
128
+ end
129
+ end
@@ -1,3 +1,5 @@
1
1
  require "tools-cf-plugin/watch"
2
- require "tools-cf-plugin/tunnel/watch"
3
- require "tools-cf-plugin/tunnel/watch_logs"
2
+ require "tools-cf-plugin/shell"
3
+ require "tools-cf-plugin/dea-ads"
4
+ require "tools-cf-plugin/tunnel/tunnel-nats"
5
+ require "tools-cf-plugin/tunnel/watch-logs"
@@ -0,0 +1,14 @@
1
+ require "cf/cli"
2
+ require "pry"
3
+ require "cli"
4
+
5
+ module CFTools
6
+ class Shell < CF::CLI
7
+ def precondition; end
8
+
9
+ desc "Launch an IRB session with client APIs available."
10
+ def shell
11
+ binding.pry :quiet => true
12
+ end
13
+ end
14
+ end
@@ -18,8 +18,6 @@ module CFTools
18
18
  def stream(locations)
19
19
  entries = entry_queue
20
20
 
21
- Thread.abort_on_exception = true
22
-
23
21
  locations.each do |(name, index), locs|
24
22
  Thread.new do
25
23
  stream_location(name, index, locs, entries)
@@ -7,15 +7,26 @@ require "cf/cli"
7
7
  require "tools-cf-plugin/tunnel/base"
8
8
 
9
9
  module CFTools::Tunnel
10
- class Watch < Base
11
- desc "Watch, by grabbing the connection info from your BOSH deployment."
10
+ class TunnelNATS < Base
11
+ def self.command_by_name
12
+ proc do |name|
13
+ @@commands[name.gsub("-", "_").to_sym] || \
14
+ fail("Unknown command '#{name}'.")
15
+ end
16
+ end
17
+
18
+ desc "Invoke another command with a local tunnel to a remote NATS."
12
19
  input :director, :argument => :required, :desc => "BOSH director address"
13
- input :gateway, :argument => :optional,
14
- :default => proc { "vcap@#{input[:director]}" },
20
+ input :command, :argument => :required, :from_given => command_by_name,
21
+ :desc => "Command to invoke"
22
+ input :args, :argument => :splat, :desc => "Arguments for wrapped command"
23
+ input :gateway, :default => proc { "vcap@#{input[:director]}" },
15
24
  :desc => "SSH connection string (default: vcap@director)"
16
- def tunnel_watch
25
+ def tunnel_nats
26
+ command = input[:command]
17
27
  director_host = input[:director]
18
28
  gateway = input[:gateway]
29
+ args = input[:args]
19
30
 
20
31
  director = connected_director(director_host, gateway)
21
32
 
@@ -35,8 +46,14 @@ module CFTools::Tunnel
35
46
  login_as_admin(manifest)
36
47
  end
37
48
 
38
- invoke :watch, :port => nport,
39
- :user => nats["user"], :password => nats["password"]
49
+ execute(
50
+ command,
51
+ args + %W[
52
+ --user #{nats["user"]}
53
+ --password #{nats["password"]}
54
+ --port #{nport}
55
+ ],
56
+ input.global)
40
57
  end
41
58
 
42
59
  private
@@ -46,7 +63,7 @@ module CFTools::Tunnel
46
63
  admin_user, admin_pass, _ = admin.split("|", 3)
47
64
 
48
65
  @@client = CFoundry::V2::Client.new(manifest["properties"]["cc"]["srv_api_uri"])
49
- client.login(admin_user, admin_pass)
66
+ @@client.login(admin_user, admin_pass)
50
67
  end
51
68
  end
52
69
  end
@@ -1,3 +1,3 @@
1
1
  module CFTools
2
- VERSION = "1.1.0".freeze
2
+ VERSION = "2.0.0".freeze
3
3
  end
@@ -0,0 +1,145 @@
1
+ require "spec_helper"
2
+
3
+ describe CFTools::DEAAds do
4
+ let(:client) { fake_client }
5
+
6
+ before { stub_client }
7
+
8
+ before do
9
+ stub(NATS).start.yields
10
+ stub(NATS).subscribe
11
+ stub(EM).add_periodic_timer.yields
12
+ end
13
+
14
+ it "subscribes to dea.advertise" do
15
+ mock(NATS).subscribe("dea.advertise")
16
+ cf %W[dea-ads]
17
+ end
18
+
19
+ it "refreshes every 3 seconds" do
20
+ mock(EM).add_periodic_timer(3).yields
21
+ mock.instance_of(described_class).render_table
22
+ cf %W[dea-ads]
23
+ end
24
+
25
+ context "when no NATS server info is specified" do
26
+ it "connects on nats:nats@127.0.0.1:4222" do
27
+ mock(NATS).start(hash_including(
28
+ :uri => "nats://nats:nats@127.0.0.1:4222"))
29
+
30
+ cf %W[dea-ads]
31
+ end
32
+ end
33
+
34
+ context "when NATS server info is specified" do
35
+ it "connects to the given location using the given credentials" do
36
+ mock(NATS).start(hash_including(
37
+ :uri => "nats://someuser:somepass@example.com:4242"))
38
+
39
+ cf %W[dea-ads -h example.com -P 4242 -u someuser -p somepass]
40
+ end
41
+ end
42
+
43
+ it "prints the table header" do
44
+ cf %W[dea-ads]
45
+ expect(output).to say(/dea\s+stacks\s+droplets\s+available memory/)
46
+ end
47
+
48
+ context "when a dea.advertise is seen" do
49
+ let(:advertise) { <<PAYLOAD }
50
+ {
51
+ "app_id_to_count": {
52
+ "id1": 2,
53
+ "id2": 1,
54
+ "id3": 3
55
+ },
56
+ "available_memory": 1256,
57
+ "stacks": [
58
+ "lucid64",
59
+ "lucid86"
60
+ ],
61
+ "prod": false,
62
+ "id": "2-1d0cf3bcd994d9f2c5ea22b9b624d77b"
63
+ }
64
+ PAYLOAD
65
+
66
+ before do
67
+ stub(NATS).subscribe.yields(advertise)
68
+ end
69
+
70
+ it "prints its entry in the table" do
71
+ cf %W[dea-ads]
72
+ expect(output).to say(/^2\s+lucid64, lucid86\s+6\s+1\.2G$/)
73
+ end
74
+
75
+ context "and another advertise is seen" do
76
+ before do
77
+ stub(NATS).subscribe do |_, blk|
78
+ blk.call(advertise)
79
+ blk.call(other_advertise)
80
+ end
81
+ end
82
+
83
+ context "from a different DEA" do
84
+ let(:other_advertise) { <<PAYLOAD }
85
+ {
86
+ "app_id_to_count": {
87
+ "id2": 3,
88
+ "id3": 1,
89
+ "id4": 1
90
+ },
91
+ "available_memory": 1024,
92
+ "stacks": [
93
+ "lucid64"
94
+ ],
95
+ "prod": false,
96
+ "id": "3-1d0cf3bcd994d9f2c5ea22b9b624d77b"
97
+ }
98
+ PAYLOAD
99
+
100
+ it "prints its entry in the table" do
101
+ cf %W[dea-ads]
102
+ expect(output).to say(/^3\s+lucid64\s+5\s+1\.0G$/)
103
+ end
104
+
105
+ it "keeps the other entry in the table" do
106
+ cf %W[dea-ads]
107
+ expect(output).to say(/^2\s+lucid64, lucid86\s+6\s+1\.2G$/)
108
+ end
109
+
110
+ it "sorts the rows by DEA index" do
111
+ cf %W[dea-ads]
112
+ expect(output).to say(/^2\s+.*\n^3/)
113
+ end
114
+ end
115
+
116
+ context "from the same DEA" do
117
+ let(:other_advertise) { <<PAYLOAD }
118
+ {
119
+ "app_id_to_count": {
120
+ "id1": 1,
121
+ "id2": 1,
122
+ "id3": 3
123
+ },
124
+ "available_memory": 1000,
125
+ "stacks": [
126
+ "lucid64"
127
+ ],
128
+ "prod": false,
129
+ "id": "2-1d0cf3bcd994d9f2c5ea22b9b624d77b"
130
+ }
131
+ PAYLOAD
132
+
133
+ it "clears the original entry" do
134
+ cf %W[dea-ads]
135
+ expect(output).to_not say("1.2G")
136
+ end
137
+
138
+ it "shows the difference from the last advertisement" do
139
+ cf %W[dea-ads]
140
+ expect(output).to say(/^2\s+lucid64\s+5 \(-1\)\s+1000\.0M \(-256\.0M\)$/)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ describe CFTools::Shell do
4
+ let(:client) { fake_client }
5
+
6
+ before { stub_client }
7
+
8
+ it "starts a pry session with :quiet" do
9
+ binding = stub
10
+
11
+ mock.instance_of(described_class).binding { binding }
12
+ mock(binding).pry :quiet => true
13
+
14
+ cf %w[shell]
15
+ end
16
+ end
@@ -0,0 +1,97 @@
1
+ require "spec_helper"
2
+
3
+ module CFTools::Tunnel
4
+ describe TunnelNATS do
5
+ let(:director) { mock }
6
+
7
+ let(:deployments) do
8
+ [{ "name" => "some-deployment", "releases" => [{ "name" => "cf-release" }] }]
9
+ end
10
+
11
+ let(:deployment) { <<MANIFEST }
12
+ ---
13
+ properties:
14
+ nats:
15
+ address: 1.2.3.4
16
+ port: 5678
17
+ user: natsuser
18
+ password: natspass
19
+ uaa:
20
+ scim:
21
+ users:
22
+ - someadmin|somepass|cloud_controller.admin
23
+ cc:
24
+ srv_api_uri: https://api.cf.museum
25
+ MANIFEST
26
+
27
+ let(:tunneled_port) { 65535 }
28
+
29
+ let(:initial_client) { stub }
30
+
31
+ before do
32
+ stub(initial_client).token { CFoundry::AuthToken.new("initial token") }
33
+
34
+ any_instance_of(described_class) do |cli|
35
+ stub(cli).connected_director { director }
36
+ stub(cli).client { initial_client }
37
+ stub(cli).tunnel_to { tunneled_port }
38
+ end
39
+
40
+ any_instance_of(CFoundry::V2::Client) do |cf|
41
+ stub(cf).login { CFoundry::AuthToken.new(nil) }
42
+ end
43
+
44
+ stub(director).list_deployments { deployments }
45
+ stub(director).get_deployment { { "manifest" => deployment } }
46
+ end
47
+
48
+ it "connects to the given director" do
49
+ any_instance_of(described_class) do |cli|
50
+ mock(cli).connected_director(
51
+ "some-director.com", "someuser@somehost.com") do
52
+ director
53
+ end
54
+ end
55
+
56
+ cf %W[tunnel-nats some-director.com help --gateway someuser@somehost.com]
57
+ end
58
+
59
+ it "tunnels to the NATS server through the director" do
60
+ any_instance_of(described_class) do |cli|
61
+ mock(cli).tunnel_to("1.2.3.4", 5678, "someuser@somehost.com") do
62
+ tunneled_port
63
+ end
64
+ end
65
+
66
+ cf %W[tunnel-nats some-director.com help --gateway someuser@somehost.com]
67
+ end
68
+
69
+ it "logs in as the admin user from the deployment" do
70
+ client = mock
71
+ stub(client).token { CFoundry::AuthToken.new("bar") }
72
+
73
+ mock(CFoundry::V2::Client).new("https://api.cf.museum") do
74
+ client
75
+ end
76
+
77
+ mock(client).login("someadmin", "somepass") do
78
+ CFoundry::AuthToken.new("foo")
79
+ end
80
+
81
+ cf %W[tunnel-nats some-director.com help]
82
+ end
83
+
84
+ it "invokes the given command with the NATS credentials" do
85
+ stub.proxy.instance_of(described_class).execute
86
+ mock.instance_of(described_class).execute(
87
+ Mothership.commands[:target],
88
+ %w[
89
+ some-arg --flag some-val --user natsuser
90
+ --password natspass --port 65535
91
+ ],
92
+ is_a(Mothership::Inputs))
93
+
94
+ cf %W[tunnel-nats some-director.com target some-arg --flag some-val]
95
+ end
96
+ end
97
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tools-cf-plugin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-28 00:00:00.000000000 Z
12
+ date: 2013-06-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cfoundry
@@ -81,6 +81,22 @@ dependencies:
81
81
  - - ! '>='
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: pry
86
+ requirement: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
84
100
  - !ruby/object:Gem::Dependency
85
101
  name: rake
86
102
  requirement: !ruby/object:Gem::Requirement
@@ -250,21 +266,26 @@ extra_rdoc_files: []
250
266
  files:
251
267
  - Rakefile
252
268
  - lib/tools-cf-plugin/plugin.rb
253
- - lib/tools-cf-plugin/tunnel/base.rb
269
+ - lib/tools-cf-plugin/version.rb
270
+ - lib/tools-cf-plugin/tunnel/watch-logs.rb
254
271
  - lib/tools-cf-plugin/tunnel/log_entry.rb
255
- - lib/tools-cf-plugin/tunnel/multi_line_stream.rb
272
+ - lib/tools-cf-plugin/tunnel/tunnel-nats.rb
256
273
  - lib/tools-cf-plugin/tunnel/stream_location.rb
257
- - lib/tools-cf-plugin/tunnel/watch.rb
258
- - lib/tools-cf-plugin/tunnel/watch_logs.rb
259
- - lib/tools-cf-plugin/version.rb
274
+ - lib/tools-cf-plugin/tunnel/base.rb
275
+ - lib/tools-cf-plugin/tunnel/multi_line_stream.rb
260
276
  - lib/tools-cf-plugin/watch.rb
277
+ - lib/tools-cf-plugin/shell.rb
278
+ - lib/tools-cf-plugin/dea-ads.rb
279
+ - spec/shell_spec.rb
280
+ - spec/watch_spec.rb
261
281
  - spec/spec_helper.rb
262
- - spec/tunnel/base_spec.rb
282
+ - spec/dea-ads_spec.rb
263
283
  - spec/tunnel/log_entry_spec.rb
264
- - spec/tunnel/multi_line_stream_spec.rb
284
+ - spec/tunnel/watch-logs_spec.rb
285
+ - spec/tunnel/base_spec.rb
265
286
  - spec/tunnel/stream_location_spec.rb
266
- - spec/tunnel/watch_logs_spec.rb
267
- - spec/watch_spec.rb
287
+ - spec/tunnel/multi_line_stream_spec.rb
288
+ - spec/tunnel/tunnel-nats_spec.rb
268
289
  homepage: http://github.com/cloudfoundry/tools-cf-plugin
269
290
  licenses: []
270
291
  post_install_message:
@@ -290,10 +311,13 @@ signing_key:
290
311
  specification_version: 3
291
312
  summary: Cloud Foundry tooling commands.
292
313
  test_files:
314
+ - spec/shell_spec.rb
315
+ - spec/watch_spec.rb
293
316
  - spec/spec_helper.rb
294
- - spec/tunnel/base_spec.rb
317
+ - spec/dea-ads_spec.rb
295
318
  - spec/tunnel/log_entry_spec.rb
296
- - spec/tunnel/multi_line_stream_spec.rb
319
+ - spec/tunnel/watch-logs_spec.rb
320
+ - spec/tunnel/base_spec.rb
297
321
  - spec/tunnel/stream_location_spec.rb
298
- - spec/tunnel/watch_logs_spec.rb
299
- - spec/watch_spec.rb
322
+ - spec/tunnel/multi_line_stream_spec.rb
323
+ - spec/tunnel/tunnel-nats_spec.rb