tools-cf-plugin 1.1.0 → 2.0.0

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.
@@ -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