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