vmc 0.5.0.beta.7 → 0.5.0.beta.10

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.
Files changed (59) hide show
  1. data/LICENSE +1277 -746
  2. data/Rakefile +22 -23
  3. data/lib/vmc/cli.rb +21 -27
  4. data/lib/vmc/cli/app/push.rb +1 -0
  5. data/lib/vmc/cli/app/push/create.rb +3 -2
  6. data/lib/vmc/cli/app/push/sync.rb +11 -8
  7. data/lib/vmc/cli/app/scale.rb +19 -17
  8. data/lib/vmc/cli/domain/map.rb +55 -0
  9. data/lib/vmc/cli/domain/unmap.rb +56 -0
  10. data/lib/vmc/cli/route/map.rb +74 -0
  11. data/lib/vmc/cli/route/unmap.rb +94 -0
  12. data/lib/vmc/cli/service/create.rb +4 -1
  13. data/lib/vmc/cli/start/base.rb +2 -2
  14. data/lib/vmc/cli/start/login.rb +5 -2
  15. data/lib/vmc/cli/start/target.rb +4 -3
  16. data/lib/vmc/cli/user/base.rb +19 -0
  17. data/lib/vmc/cli/user/passwd.rb +7 -19
  18. data/lib/vmc/cli/{start → user}/register.rb +12 -23
  19. data/lib/vmc/constants.rb +1 -1
  20. data/lib/vmc/test_support.rb +4 -0
  21. data/lib/vmc/test_support/command_helper.rb +38 -0
  22. data/{spec/support → lib/vmc/test_support}/common_input_examples.rb +0 -0
  23. data/lib/vmc/test_support/fake_home_dir.rb +16 -0
  24. data/lib/vmc/test_support/interact_helper.rb +29 -0
  25. data/lib/vmc/version.rb +1 -1
  26. data/spec/features/new_user_flow_spec.rb +43 -51
  27. data/spec/spec_helper.rb +24 -12
  28. data/spec/vmc/cli/app/instances_spec.rb +3 -8
  29. data/spec/vmc/cli/app/push/create_spec.rb +10 -7
  30. data/spec/vmc/cli/app/push_spec.rb +1 -1
  31. data/spec/vmc/cli/app/rename_spec.rb +1 -1
  32. data/spec/vmc/cli/app/scale_spec.rb +81 -0
  33. data/spec/vmc/cli/app/stats_spec.rb +3 -7
  34. data/spec/vmc/cli/domain/map_spec.rb +140 -0
  35. data/spec/vmc/cli/domain/unmap_spec.rb +73 -0
  36. data/spec/vmc/cli/organization/orgs_spec.rb +13 -16
  37. data/spec/vmc/cli/organization/rename_spec.rb +1 -1
  38. data/spec/vmc/cli/route/map_spec.rb +142 -0
  39. data/spec/vmc/cli/route/unmap_spec.rb +215 -0
  40. data/spec/vmc/cli/service/rename_spec.rb +1 -1
  41. data/spec/vmc/cli/space/rename_spec.rb +15 -18
  42. data/spec/vmc/cli/space/spaces_spec.rb +18 -25
  43. data/spec/vmc/cli/start/info_spec.rb +44 -46
  44. data/spec/vmc/cli/start/login_spec.rb +40 -0
  45. data/spec/vmc/cli/user/create_spec.rb +54 -0
  46. data/spec/vmc/cli/user/passwd_spec.rb +7 -14
  47. data/spec/vmc/cli/{start → user}/register_spec.rb +26 -22
  48. data/spec/vmc/cli_spec.rb +164 -6
  49. metadata +46 -39
  50. data/lib/vmc/cli/app/routes.rb +0 -100
  51. data/lib/vmc/cli/domain/add_domain.rb +0 -25
  52. data/lib/vmc/cli/domain/create_domain.rb +0 -28
  53. data/lib/vmc/cli/domain/delete_domain.rb +0 -56
  54. data/lib/vmc/cli/domain/remove_domain.rb +0 -28
  55. data/lib/vmc/cli/route/create_route.rb +0 -49
  56. data/lib/vmc/cli/route/delete.rb +0 -47
  57. data/spec/support/feature_helpers.rb +0 -16
  58. data/spec/support/interact_helpers.rb +0 -27
  59. data/spec/vmc/cli/route/delete_route_spec.rb +0 -162
@@ -21,9 +21,7 @@ describe VMC::Organization::Orgs do
21
21
  end
22
22
 
23
23
  subject do
24
- with_output_to output do
25
- Mothership.new.invoke(:orgs, inputs, given, global)
26
- end
24
+ capture_output { Mothership.new.invoke(:orgs, inputs, given, global) }
27
25
  end
28
26
 
29
27
  describe 'metadata' do
@@ -47,10 +45,9 @@ describe VMC::Organization::Orgs do
47
45
 
48
46
  it 'should have the correct first two lines' do
49
47
  subject
50
-
51
- output.rewind
52
- expect(output.readline).to match /Getting organizations.*OK/
53
- expect(output.readline).to eq "\n"
48
+ stdout.rewind
49
+ expect(stdout.readline).to match /Getting organizations.*OK/
50
+ expect(stdout.readline).to eq "\n"
54
51
  end
55
52
 
56
53
  context 'when there are no orgnaizations' do
@@ -71,9 +68,9 @@ describe VMC::Organization::Orgs do
71
68
  it 'should show only the progress' do
72
69
  subject
73
70
 
74
- output.rewind
75
- expect(output.readline).to match /Getting organizations.*OK/
76
- expect(output).to be_eof
71
+ stdout.rewind
72
+ expect(stdout.readline).to match /Getting organizations.*OK/
73
+ expect(stdout).to be_eof
77
74
  end
78
75
  end
79
76
  end
@@ -96,15 +93,15 @@ describe VMC::Organization::Orgs do
96
93
  it 'displays tabular output with names, spaces and domains' do
97
94
  subject
98
95
 
99
- output.rewind
100
- output.readline
101
- output.readline
96
+ stdout.rewind
97
+ stdout.readline
98
+ stdout.readline
102
99
 
103
- expect(output.readline).to match /name\s+spaces\s+domains/
100
+ expect(stdout.readline).to match /name\s+spaces\s+domains/
104
101
  organizations.sort_by(&:name).each do |org|
105
- expect(output.readline).to match /#{org.name}\s+#{name_list(org.spaces)}\s+#{name_list(org.domains)}/
102
+ expect(stdout.readline).to match /#{org.name}\s+#{name_list(org.spaces)}\s+#{name_list(org.domains)}/
106
103
  end
107
- expect(output).to be_eof
104
+ expect(stdout).to be_eof
108
105
  end
109
106
  end
110
107
  end
@@ -104,7 +104,7 @@ describe VMC::Organization::Rename do
104
104
 
105
105
  context 'and the name already exists' do
106
106
  it 'fails' do
107
- mock(renamed_organization).update! { raise CFoundry::OrganizationNameTaken.new(nil, nil, "Bad error", 200) }
107
+ mock(renamed_organization).update! { raise CFoundry::OrganizationNameTaken.new("Bad error", 200) }
108
108
  expect { subject }.to raise_error(CFoundry::OrganizationNameTaken)
109
109
  end
110
110
  end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::Route::Map do
4
+ let(:global) { { :color => false } }
5
+ let(:given) { {} }
6
+ let(:client) { fake_client }
7
+ let!(:cli) { described_class.new }
8
+
9
+ before do
10
+ stub(cli).client { client }
11
+ stub_output(cli)
12
+ end
13
+
14
+ let(:app){ fake(:app, :space => space, :name => "app-name") }
15
+ let(:space) { fake(:space, :name => "space-name", :domains => space_domains) }
16
+ let(:domain) { fake(:domain, :name => domain_name ) }
17
+ let(:domain_name) { "some-domain.com" }
18
+ let(:host_name) { "some-host" }
19
+ let(:url) { "#{host_name}.#{domain_name}" }
20
+ let(:space_domains) { [] }
21
+
22
+ subject { invoke_cli(cli, :map, inputs, given, global) }
23
+
24
+ context 'when targeting v2' do
25
+ shared_examples "mapping the route to the app" do
26
+ context 'and the domain is mapped to the space' do
27
+ let(:space_domains) { [domain] }
28
+
29
+ context 'and the route is mapped to the space' do
30
+ let(:client) { fake_client :routes => [route] }
31
+ let(:route) { fake(:route, :space => space, :host => host_name, :domain => domain) }
32
+
33
+ it 'binds the route to the app' do
34
+ mock(app).add_route(route)
35
+ subject
36
+ end
37
+ end
38
+
39
+ context 'and the route is not mapped to the space' do
40
+ let(:new_route) { fake(:route) }
41
+
42
+ before do
43
+ stub(client).route { new_route }
44
+ stub(app).add_route
45
+ stub(new_route).create!
46
+ end
47
+
48
+ it 'indicates that it is creating a route' do
49
+ mock(cli).print("Creating route #{host_name}.#{domain_name}")
50
+ subject
51
+ end
52
+
53
+ it "creates the route in the app's space" do
54
+ mock(new_route).create!
55
+ subject
56
+ expect(new_route.host).to eq host_name
57
+ expect(new_route.domain).to eq domain
58
+ expect(new_route.space).to eq space
59
+ end
60
+
61
+ it 'indicates that it is binding the route' do
62
+ mock(cli).print("Binding #{host_name}.#{domain_name} to app-name")
63
+ subject
64
+ end
65
+
66
+ it 'binds the route to the app' do
67
+ mock(app).add_route(new_route)
68
+ subject
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'when an app is specified' do
75
+ let(:inputs) { { :url => url, :app => app } }
76
+
77
+ context 'and the domain is not already mapped to the space' do
78
+ it 'indicates that the domain is invalid' do
79
+ expect { subject }.to raise_error(VMC::UserError, /Invalid domain/)
80
+ end
81
+ end
82
+
83
+ include_examples "mapping the route to the app"
84
+ end
85
+
86
+ context 'when a space is specified' do
87
+ let(:inputs) { { :url => url, :space => space } }
88
+
89
+ context 'and the domain is not mapped to the space' do
90
+ it 'indicates that the domain is invalid' do
91
+ expect { subject }.to raise_error(VMC::UserError, /Invalid domain/)
92
+ end
93
+ end
94
+
95
+ context 'and the domain is mapped to the space' do
96
+ let(:domain) { fake(:domain, :client => client, :name => domain_name ) }
97
+ let(:new_route) { fake(:route, :host => "new-route-host") }
98
+
99
+ before do
100
+ stub(client).route { new_route }
101
+ stub(new_route).create!
102
+ stub(space).domain_by_name(domain_name, anything) { domain }
103
+ end
104
+
105
+ context 'and the route does not exist' do
106
+ it 'indicates that it is creating a route' do
107
+ mock(cli).print("Creating route #{host_name}.#{domain_name}")
108
+ subject
109
+ end
110
+
111
+ it 'creates the route in the given space' do
112
+ mock(new_route).create!
113
+ subject
114
+ expect(new_route.host).to eq host_name
115
+ expect(new_route.domain).to eq domain
116
+ expect(new_route.space).to eq space
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ context 'when neither an app nor a space is specified' do
123
+ let(:inputs) { { :url => url } }
124
+ let(:space_domains) { [domain] }
125
+ let(:new_route) { fake(:route) }
126
+
127
+ before { stub_ask("Which application?", anything) { app } }
128
+
129
+ it 'asks for an app' do
130
+ stub(client).route { new_route }
131
+ stub(app).add_route
132
+ stub(new_route).create!
133
+ mock_ask("Which application?", anything) { app }
134
+ subject
135
+ end
136
+
137
+ include_examples "mapping the route to the app"
138
+ end
139
+ end
140
+
141
+ context 'when targeting v1'
142
+ end
@@ -0,0 +1,215 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::Route::Unmap do
4
+ let(:global) { { :color => false } }
5
+ let(:given) { {} }
6
+ let(:inputs) { {} }
7
+ let(:client) { fake_client }
8
+ let!(:cli) { described_class.new }
9
+ let(:app){ fake(:app, :space => space, :name => "app-name") }
10
+ let(:space) { fake(:space, :name => "space-name", :domains => space_domains) }
11
+ let(:domain) { fake(:domain, :name => domain_name ) }
12
+ let(:domain_name) { "some-domain.com" }
13
+ let(:host_name) { "some-host" }
14
+ let(:url) { "#{host_name}.#{domain_name}" }
15
+ let(:space_domains) { [domain] }
16
+
17
+ before do
18
+ stub(cli).client { client }
19
+ stub_output(cli)
20
+ end
21
+
22
+ subject { invoke_cli(cli, :unmap, inputs, given, global) }
23
+
24
+ describe 'metadata' do
25
+ let(:command) { Mothership.commands[:delete_service] }
26
+
27
+ describe 'command' do
28
+ subject { command }
29
+ its(:description) { should eq "Delete a service" }
30
+ it { expect(Mothership::Help.group(:services, :manage)).to include(subject) }
31
+ end
32
+
33
+ include_examples 'inputs must have descriptions'
34
+
35
+ describe 'arguments' do
36
+ subject { command.arguments }
37
+ it 'has the correct argument order' do
38
+ should eq([{:type => :optional, :value => nil, :name => :service }])
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'when targeting v2' do
44
+ context "when an app and a url are specified" do
45
+ let(:given) { { :url => url } }
46
+ let(:inputs) { { :app => app } }
47
+
48
+ context "when the given route is mapped to the given app" do
49
+ let(:client) { fake_client :routes => [route] }
50
+ let(:app) { fake(:app, :space => space, :name => "app-name", :routes => [route]) }
51
+ let(:route) { fake(:route, :space => space, :host => host_name, :domain => domain) }
52
+
53
+ it "unmaps the url from the app" do
54
+ mock(app).remove_route(route)
55
+ subject
56
+ end
57
+ end
58
+
59
+ context "when the given route is NOT mapped to the given app" do
60
+ it "displays an error" do
61
+ expect { subject }.to raise_error(VMC::UserError, /Unknown route/)
62
+ end
63
+ end
64
+ end
65
+
66
+ context "when only an app is specified" do
67
+ let(:other_route) { fake(:route, :host => "abcd", :domain => domain) }
68
+ let(:route) { fake(:route, :host => "efgh", :domain => domain) }
69
+ let(:app) { fake(:app, :space => space, :routes => [route, other_route] )}
70
+ let(:inputs) { { :app => app } }
71
+
72
+ before { stub(app).remove_route(route) }
73
+
74
+ it "asks the user to select from the app's urls" do
75
+ mock_ask("Which URL?", anything) do |_, opts|
76
+ expect(opts[:choices]).to eq [other_route, route]
77
+ route
78
+ end
79
+
80
+ subject
81
+ end
82
+
83
+ it "unmaps the selected url from the app" do
84
+ stub_ask("Which URL?", anything) { route }
85
+ mock(app).remove_route(route)
86
+ subject
87
+ end
88
+ end
89
+
90
+ context "when an app is specified and the --all option is given" do
91
+ let(:other_route) { fake(:route, :host => "abcd", :domain => domain) }
92
+ let(:route) { fake(:route, :host => "efgh", :domain => domain) }
93
+ let(:app) { fake(:app, :routes => [route, other_route]) }
94
+ let(:inputs) { { :app => app, :all => true } }
95
+
96
+ it "unmaps all routes from the given app" do
97
+ mock(app).remove_route(route)
98
+ mock(app).remove_route(other_route)
99
+ subject
100
+ end
101
+ end
102
+
103
+ context "when a url is specified and the --delete option is given" do
104
+ let(:route) { fake(:route, :host => host_name, :domain => domain) }
105
+ let(:client) { fake_client :routes => [route] }
106
+ let(:given) { { :url => url } }
107
+ let(:inputs) { { :delete => true } }
108
+
109
+ it "deletes the route" do
110
+ mock(route).delete!
111
+ subject
112
+ end
113
+ end
114
+
115
+ context "when the --delete and --all options are both passed" do
116
+ let(:inputs) { { :delete => true, :all => true } }
117
+ let(:other_route) { fake(:route, :host => "abcd", :domain => domain) }
118
+ let(:route) { fake(:route, :host => "efgh", :domain => domain) }
119
+ let(:client) { fake_client :routes => [route, other_route] }
120
+
121
+ it "asks if the user really wants to unmap all urls" do
122
+ mock_ask("Really delete ALL URLS?", :default => false) { false }
123
+ subject
124
+ end
125
+
126
+ context "when the user responds with a yes" do
127
+ before { stub_ask("Really delete ALL URLS?", anything) { true } }
128
+
129
+ it "deletes all the user's routes" do
130
+ client.routes.each { |r| mock(r).delete! }
131
+ subject
132
+ end
133
+ end
134
+
135
+ context "when the user responds with a no" do
136
+ before { stub_ask("Really delete ALL URLS?", anything) { false } }
137
+
138
+ it "does not delete any routes" do
139
+ any_instance_of(route.class) do |route|
140
+ dont_allow(route).delete!
141
+ end
142
+ subject
143
+ end
144
+ end
145
+ end
146
+
147
+ context "when only a url is passed" do
148
+ let(:route) { fake(:route, :host => host_name, :domain => domain) }
149
+ let(:client) { fake_client :routes => [route] }
150
+ let(:given) { { :url => url } }
151
+
152
+ it "displays an error message" do
153
+ expect { subject }.to raise_error(VMC::UserError, /Missing either --delete or --app/)
154
+ end
155
+ end
156
+ end
157
+
158
+ context 'when targeting v1' do
159
+ let(:client) { CFoundry::V1::Client.new }
160
+ let(:app) { CFoundry::V1::App.new("some-app", client) }
161
+ let(:other_url) { "some.other.url.com" }
162
+
163
+ context "when an app and a url are specified" do
164
+ let(:given) { { :url => url } }
165
+ let(:inputs) { { :app => app } }
166
+
167
+ context "when the given url is not mapped to the app" do
168
+ before { app.urls = [other_url] }
169
+
170
+ it "displays an error message" do
171
+ expect { subject }.to raise_error(VMC::UserError, /URL.*not mapped/)
172
+ end
173
+ end
174
+
175
+ context "when the given url is mapped to the app" do
176
+ before { app.urls = [url, other_url] }
177
+
178
+ it "unmaps the url from the app" do
179
+ mock(app).update!
180
+ subject
181
+ expect(app.urls).to eq [other_url]
182
+ end
183
+ end
184
+ end
185
+
186
+ context "when only an app is specified" do
187
+ let(:inputs) { { :app => app } }
188
+ before { app.urls = [url, other_url] }
189
+
190
+ it "asks for the url" do
191
+ mock_ask("Which URL?", :choices => [url, other_url]) { url }
192
+ stub(app).update!
193
+ subject
194
+ end
195
+
196
+ it "unmaps the selected url from the app" do
197
+ stub_ask("Which URL?", anything) { url }
198
+ mock(app).update!
199
+ subject
200
+ expect(app.urls).to eq [other_url]
201
+ end
202
+ end
203
+
204
+ context "when an app is specified and the --all option is given" do
205
+ let(:inputs) { { :app => app, :all => true } }
206
+
207
+ it "unmaps all routes from the given app" do
208
+ app.urls = ["foo", "bar"]
209
+ mock(app).update!
210
+ subject
211
+ expect(app.urls).to eq []
212
+ end
213
+ end
214
+ end
215
+ end
@@ -95,7 +95,7 @@ describe VMC::Service::Rename do
95
95
 
96
96
  context 'and the name already exists' do
97
97
  it 'fails' do
98
- mock(renamed_service).update! { raise CFoundry::ServiceInstanceNameTaken.new nil, nil, "Taken", 200 }
98
+ mock(renamed_service).update! { raise CFoundry::ServiceInstanceNameTaken.new("Taken", 200) }
99
99
  expect { subject }.to raise_error(CFoundry::ServiceInstanceNameTaken)
100
100
  end
101
101
  end
@@ -2,23 +2,18 @@ require 'spec_helper'
2
2
  require "vmc/cli/space/rename"
3
3
 
4
4
  describe VMC::Space::Rename do
5
- let(:global) { { :color => false, :quiet => true } }
6
- let(:inputs) { {} }
7
- let(:given) { {} }
8
5
  let(:spaces) { fake_list(:space, 3) }
9
6
  let(:organization) { fake(:organization, :spaces => spaces) }
10
7
  let(:client) { fake_client(:current_organization => organization, :spaces => spaces) }
11
8
  let(:new_name) { "some-new-name" }
12
9
 
13
10
  before do
14
- any_instance_of(VMC::CLI) do |cli|
11
+ any_instance_of described_class do |cli|
15
12
  stub(cli).client { client }
16
13
  stub(cli).precondition { nil }
17
14
  end
18
15
  end
19
16
 
20
- subject { Mothership.new.invoke(:rename_space, inputs, given, global) }
21
-
22
17
  describe 'metadata' do
23
18
  let(:command) { Mothership.commands[:rename_space] }
24
19
 
@@ -45,12 +40,19 @@ describe VMC::Space::Rename do
45
40
  let(:spaces) { [] }
46
41
 
47
42
  context 'and a space is given' do
48
- let(:given) { { :space => "some-invalid-space" } }
49
- it { expect { subject }.to raise_error(VMC::UserError, "Unknown space 'some-invalid-space'.") }
43
+ subject { vmc %W[rename-space --space some-invalid-space --no-force --no-quiet] }
44
+ it 'prints out an error message' do
45
+ subject
46
+ expect(stderr.string).to include "Unknown space 'some-invalid-space'."
47
+ end
50
48
  end
51
49
 
52
50
  context 'and a space is not given' do
53
- it { expect { subject }.to raise_error(VMC::UserError, "No spaces.") }
51
+ subject { vmc %W[rename-space --no-force] }
52
+ it 'prints out an error message' do
53
+ subject
54
+ expect(stderr.string).to include "No spaces."
55
+ end
54
56
  end
55
57
  end
56
58
 
@@ -58,6 +60,8 @@ describe VMC::Space::Rename do
58
60
  let(:renamed_space) { spaces.first }
59
61
 
60
62
  context 'when the defaults are used' do
63
+ subject { vmc %W[rename-space --no-force --no-quiet] }
64
+
61
65
  it 'asks for the space and new name and renames' do
62
66
  mock_ask("Rename which space?", anything) { renamed_space }
63
67
  mock_ask("New name") { new_name }
@@ -68,7 +72,7 @@ describe VMC::Space::Rename do
68
72
  end
69
73
 
70
74
  context 'when no name is provided, but a space is' do
71
- let(:given) { { :space => renamed_space.name } }
75
+ subject { vmc %W[rename-space --space #{renamed_space.name} --no-force] }
72
76
 
73
77
  it 'asks for the new name and renames' do
74
78
  dont_allow_ask("Rename which space?", anything)
@@ -80,7 +84,7 @@ describe VMC::Space::Rename do
80
84
  end
81
85
 
82
86
  context 'when a space is provided and a name' do
83
- let(:inputs) { { :space => renamed_space, :name => new_name } }
87
+ subject { vmc %W[rename-space --space #{renamed_space.name} --name #{new_name} --no-force] }
84
88
 
85
89
  it 'renames the space' do
86
90
  mock(renamed_space).update!
@@ -93,13 +97,6 @@ describe VMC::Space::Rename do
93
97
 
94
98
  subject
95
99
  end
96
-
97
- context 'and the name already exists' do
98
- it 'fails' do
99
- mock(renamed_space).update! { raise CFoundry::SpaceNameTaken.new nil, nil, "Taken", 404 }
100
- expect { subject }.to raise_error(CFoundry::SpaceNameTaken)
101
- end
102
- end
103
100
  end
104
101
  end
105
102
  end