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.
- data/lib/tools-cf-plugin/dea-ads.rb +129 -0
- data/lib/tools-cf-plugin/plugin.rb +4 -2
- data/lib/tools-cf-plugin/shell.rb +14 -0
- data/lib/tools-cf-plugin/tunnel/multi_line_stream.rb +0 -2
- data/lib/tools-cf-plugin/tunnel/{watch.rb → tunnel-nats.rb} +25 -8
- data/lib/tools-cf-plugin/tunnel/{watch_logs.rb → watch-logs.rb} +0 -0
- data/lib/tools-cf-plugin/version.rb +1 -1
- data/spec/dea-ads_spec.rb +145 -0
- data/spec/shell_spec.rb +16 -0
- data/spec/tunnel/tunnel-nats_spec.rb +97 -0
- data/spec/tunnel/{watch_logs_spec.rb → watch-logs_spec.rb} +0 -0
- metadata +39 -15
@@ -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/
|
3
|
-
require "tools-cf-plugin/
|
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"
|
@@ -7,15 +7,26 @@ require "cf/cli"
|
|
7
7
|
require "tools-cf-plugin/tunnel/base"
|
8
8
|
|
9
9
|
module CFTools::Tunnel
|
10
|
-
class
|
11
|
-
|
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 :
|
14
|
-
:
|
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
|
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
|
-
|
39
|
-
|
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
|
File without changes
|
@@ -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
|
data/spec/shell_spec.rb
ADDED
@@ -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
|
File without changes
|
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:
|
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-
|
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/
|
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/
|
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/
|
258
|
-
- lib/tools-cf-plugin/tunnel/
|
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/
|
282
|
+
- spec/dea-ads_spec.rb
|
263
283
|
- spec/tunnel/log_entry_spec.rb
|
264
|
-
- spec/tunnel/
|
284
|
+
- spec/tunnel/watch-logs_spec.rb
|
285
|
+
- spec/tunnel/base_spec.rb
|
265
286
|
- spec/tunnel/stream_location_spec.rb
|
266
|
-
- spec/tunnel/
|
267
|
-
- spec/
|
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/
|
317
|
+
- spec/dea-ads_spec.rb
|
295
318
|
- spec/tunnel/log_entry_spec.rb
|
296
|
-
- spec/tunnel/
|
319
|
+
- spec/tunnel/watch-logs_spec.rb
|
320
|
+
- spec/tunnel/base_spec.rb
|
297
321
|
- spec/tunnel/stream_location_spec.rb
|
298
|
-
- spec/tunnel/
|
299
|
-
- spec/
|
322
|
+
- spec/tunnel/multi_line_stream_spec.rb
|
323
|
+
- spec/tunnel/tunnel-nats_spec.rb
|