vmc 0.5.0.rc1 → 0.5.0.rc2

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 (38) hide show
  1. data/lib/vmc/cli.rb +2 -1
  2. data/lib/vmc/cli/app/start.rb +81 -24
  3. data/lib/vmc/cli/space/create.rb +4 -0
  4. data/lib/vmc/cli/space/switch.rb +16 -0
  5. data/lib/vmc/cli/start/base.rb +21 -18
  6. data/lib/vmc/cli/user/base.rb +1 -1
  7. data/lib/vmc/cli/user/delete.rb +2 -0
  8. data/lib/vmc/version.rb +1 -1
  9. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  10. data/spec/features/{new_user_flow_spec.rb → v1/new_user_flow_spec.rb} +12 -18
  11. data/spec/features/v2/account_lifecycle_spec.rb +64 -0
  12. data/spec/features/v2/push_flow_spec.rb +126 -0
  13. data/spec/spec_helper.rb +16 -3
  14. data/{lib/vmc/test_support → spec/support}/command_helper.rb +5 -5
  15. data/spec/support/config_helper.rb +15 -0
  16. data/spec/support/console_app_specker_matchers.rb +2 -2
  17. data/spec/support/fake_home_dir.rb +42 -0
  18. data/{lib/vmc/test_support → spec/support}/interact_helper.rb +1 -1
  19. data/spec/support/shared_examples/errors.rb +40 -0
  20. data/{lib/vmc/test_support/common_input_examples.rb → spec/support/shared_examples/input.rb} +0 -0
  21. data/spec/support/specker_runner.rb +17 -61
  22. data/spec/support/tracking_expector.rb +71 -0
  23. data/spec/vmc/cli/app/instances_spec.rb +3 -3
  24. data/spec/vmc/cli/app/start_spec.rb +201 -0
  25. data/spec/vmc/cli/app/stats_spec.rb +21 -15
  26. data/spec/vmc/cli/space/create_spec.rb +73 -0
  27. data/spec/vmc/cli/space/switch_space_spec.rb +55 -0
  28. data/spec/vmc/cli/start/info_spec.rb +3 -16
  29. data/spec/vmc/cli/start/login_spec.rb +97 -37
  30. data/spec/vmc/cli/start/logout_spec.rb +3 -16
  31. data/spec/vmc/cli/start/target_spec.rb +84 -0
  32. data/spec/vmc/cli/user/delete_spec.rb +51 -0
  33. data/spec/vmc/cli/user/passwd_spec.rb +1 -1
  34. data/spec/vmc/cli/user/register_spec.rb +1 -1
  35. data/spec/vmc/cli_spec.rb +38 -36
  36. metadata +65 -39
  37. data/lib/vmc/cli/space/take.rb +0 -16
  38. data/lib/vmc/test_support/fake_home_dir.rb +0 -16
data/lib/vmc/cli.rb CHANGED
@@ -26,7 +26,7 @@ module VMC
26
26
  option :help, :desc => "Show command usage", :alias => "-h",
27
27
  :default => false
28
28
 
29
- option :proxy, :desc => "Act as another user (admin)", :alias => "-u",
29
+ option :proxy, :desc => "Run this command as another user (admin)", :alias => "-u",
30
30
  :value => :email
31
31
 
32
32
  option :version, :desc => "Print version number", :alias => "-v",
@@ -398,6 +398,7 @@ module VMC
398
398
  @@client =
399
399
  case info[:version]
400
400
  when 2
401
+ fail "User switching not implemented for v2." if input[:proxy]
401
402
  CFoundry::V2::Client.new(target, token)
402
403
  when 1
403
404
  CFoundry::V1::Client.new(target, token)
@@ -19,16 +19,13 @@ module VMC::App
19
19
 
20
20
  switch_mode(app, input[:debug_mode])
21
21
 
22
- with_progress("Starting #{c(app.name, :name)}") do |s|
23
- if app.started?
24
- s.skip do
25
- err "Already started."
26
- end
27
- end
28
-
29
- app.start!
22
+ if app.started?
23
+ err "Application #{b(app.name)} is already started."
24
+ next
30
25
  end
31
26
 
27
+ log = start_app(app)
28
+ stream_start_log(log) if log
32
29
  check_application(app)
33
30
 
34
31
  if app.debug_mode && !quiet?
@@ -38,6 +35,33 @@ module VMC::App
38
35
  end
39
36
  end
40
37
 
38
+ private
39
+
40
+ def start_app(app)
41
+ log = nil
42
+ with_progress("Starting #{c(app.name, :name)}") do
43
+ app.start!(true) do |url|
44
+ log = url
45
+ end
46
+ end
47
+ log
48
+ end
49
+
50
+ def stream_start_log(log)
51
+ offset = 0
52
+
53
+ while true
54
+ begin
55
+ client.stream_url(log + "&tail&tail_offset=#{offset}") do |out|
56
+ offset += out.size
57
+ print out
58
+ end
59
+ rescue Timeout::Error
60
+ end
61
+ end
62
+ rescue CFoundry::APIError
63
+ end
64
+
41
65
  # set app debug mode, ensuring it's valid, and shutting it down
42
66
  def switch_mode(app, mode)
43
67
  mode = nil if mode == "none"
@@ -68,26 +92,59 @@ module VMC::App
68
92
  end
69
93
 
70
94
  def check_application(app)
71
- with_progress("Checking #{c(app.name, :name)}") do |s|
72
- if app.debug_mode == "suspend"
73
- s.skip do
74
- line "Application is in suspended debugging mode."
75
- line "It will wait for you to attach to it before starting."
76
- end
95
+ if app.debug_mode == "suspend"
96
+ line "Application is in suspended debugging mode."
97
+ line "It will wait for you to attach to it before starting."
98
+ return
99
+ end
100
+
101
+ line "Checking #{c(app.name, :name)}..."
102
+
103
+ seconds = 0
104
+ while instances = app.instances
105
+ indented { print_instances_summary(instances) }
106
+
107
+ if all_instances_running?(instances)
108
+ line "#{c("OK", :good)}"
109
+ return
77
110
  end
78
111
 
79
- seconds = 0
80
- until app.healthy?
81
- sleep 1
82
- seconds += 1
83
- if seconds == APP_CHECK_LIMIT
84
- s.give_up do
85
- err "Application failed to start."
86
- # TODO: print logs
87
- end
88
- end
112
+ if any_instance_flapping?(instances) || seconds == APP_CHECK_LIMIT
113
+ err "Application failed to start."
114
+ return
115
+ end
116
+
117
+ sleep 1
118
+ seconds += 1
119
+ end
120
+ end
121
+
122
+ def all_instances_running?(instances)
123
+ instances.all? { |i| i.state == "RUNNING" }
124
+ end
125
+
126
+ def any_instance_flapping?(instances)
127
+ instances.any? { |i| i.state == "FLAPPING" }
128
+ end
129
+
130
+ def print_instances_summary(instances)
131
+ counts = Hash.new { 0 }
132
+ instances.each do |i|
133
+ counts[i.state] += 1
134
+ end
135
+
136
+ states = []
137
+ %w{RUNNING STARTING DOWN FLAPPING}.each do |state|
138
+ if (num = counts[state]) > 0
139
+ states << "#{b(num)} #{c(state.downcase, state_color(state))}"
89
140
  end
90
141
  end
142
+
143
+ total = instances.count
144
+ running = counts["RUNNING"].to_s.rjust(total.to_s.size)
145
+
146
+ ratio = "#{running}#{d("/")}#{total} instances:"
147
+ line "#{ratio} #{states.join(", ")}"
91
148
  end
92
149
  end
93
150
  end
@@ -14,7 +14,11 @@ module VMC::Space
14
14
  input :manager, :desc => "Add yourself as manager", :default => true
15
15
  input :developer, :desc => "Add yourself as developer", :default => true
16
16
  input :auditor, :desc => "Add yourself as auditor", :default => false
17
+
17
18
  def create_space
19
+ return invoke :help,
20
+ :command => "create-space" if input[:organization].nil?
21
+
18
22
  space = client.space
19
23
  space.organization = input[:organization]
20
24
  space.name = input[:name]
@@ -0,0 +1,16 @@
1
+ require "vmc/cli/space/base"
2
+
3
+ module VMC::Space
4
+ class Switch < Base
5
+ desc "Switch to a space"
6
+ group :spaces, :hidden => true
7
+ input :name, :desc => "Space name", :argument => true
8
+ def switch_space
9
+ if (space = client.space_by_name(input[:name]))
10
+ invoke :target, :space => space
11
+ else
12
+ raise VMC::UserError, "The space #{input[:name]} does not exist, please create the space first."
13
+ end
14
+ end
15
+ end
16
+ end
@@ -34,34 +34,37 @@ module VMC
34
34
  end
35
35
  end
36
36
 
37
- def select_org_and_space(input, info)
38
- changed_org = false
39
-
37
+ def select_org(input, info)
40
38
  if input.has?(:organization) || !org_valid?(info[:organization])
41
39
  org = input[:organization]
42
- return unless org
43
-
44
- with_progress("Switching to organization #{c(org.name, :name)}") do
45
- info[:organization] = org.guid
46
- changed_org = true
47
- end
40
+ with_progress("Switching to organization #{c(org.name, :name)}") {} if org
41
+ org
48
42
  else
49
- org = client.current_organization
43
+ client.current_organization
50
44
  end
45
+ end
51
46
 
52
- # switching org means switching space
53
- if changed_org || input.has?(:space) || !space_valid?(info[:space])
47
+ def select_space(org, input, info, changed_org)
48
+ if input.has?(:space) || !space_valid?(info[:space])
54
49
  line if changed_org && !quiet?
55
-
56
50
  space = input[:space, org]
57
- return unless space
58
-
59
- with_progress("Switching to space #{c(space.name, :name)}") do
60
- info[:space] = space.guid
61
- end
51
+ with_progress("Switching to space #{c(space.name, :name)}") {} if space
52
+ space
53
+ else
54
+ client.current_space
62
55
  end
63
56
  end
64
57
 
58
+ def select_org_and_space(input, info)
59
+ org = select_org(input, info)
60
+ changed_org = client.current_organization != org
61
+ space = select_space(org, input, info, changed_org) if org
62
+ info.merge!(
63
+ :organization => (org ? org.guid : nil),
64
+ :space => (space ? space.guid : nil)
65
+ )
66
+ end
67
+
65
68
  def org_valid?(guid, user = client.current_user)
66
69
  return false unless guid
67
70
  client.organization(guid).users.include? user
@@ -19,7 +19,7 @@ module VMC
19
19
  end
20
20
 
21
21
  def validate_password_strength!(password)
22
- strength = client.password_score(password)
22
+ strength = client.base.password_score(password)
23
23
  msg = "Your password strength is: #{strength}"
24
24
  fail msg if strength == :weak
25
25
  line msg
@@ -8,6 +8,8 @@ module VMC::User
8
8
  input :really, :type => :boolean, :forget => true, :hidden => true,
9
9
  :default => proc { force? || interact }
10
10
  def delete_user
11
+ no_v2
12
+
11
13
  email = input[:email]
12
14
  return unless input[:really, email]
13
15
 
data/lib/vmc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module VMC
2
- VERSION = "0.5.0.rc1".freeze
2
+ VERSION = "0.5.0.rc2".freeze
3
3
  end
@@ -0,0 +1,17 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ rack (1.5.2)
5
+ rack-protection (1.3.2)
6
+ rack
7
+ sinatra (1.3.4)
8
+ rack (~> 1.4)
9
+ rack-protection (~> 1.3)
10
+ tilt (~> 1.3, >= 1.3.3)
11
+ tilt (1.3.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ sinatra
@@ -1,18 +1,18 @@
1
1
  require 'spec_helper'
2
- require "webmock/rspec"
3
-
4
- INTEGRATE_WITH = ENV["INTEGRATE_WITH"] || "default"
2
+ require 'webmock/rspec'
3
+ require 'ffaker'
5
4
 
6
5
  if ENV['VMC_TEST_USER'] && ENV['VMC_TEST_PASSWORD'] && ENV['VMC_TEST_TARGET']
7
- describe 'A new user tries to use VMC against v1 production', :ruby19 => true do
6
+ describe 'A new user tries to use VMC against v1 production', :ruby19 => true do
8
7
  include ConsoleAppSpeckerMatchers
9
8
  include VMC::Interactive
10
9
 
10
+ let(:output) { StringIO.new }
11
+ let(:out) { output.string.strip_progress_dots }
12
+
11
13
  let(:target) { ENV['VMC_TEST_TARGET'] }
12
14
  let(:username) { ENV['VMC_TEST_USER'] }
13
15
  let(:password) { ENV['VMC_TEST_PASSWORD'] }
14
- let(:output) { StringIO.new }
15
- let(:out) { output.string.strip_progress_dots }
16
16
 
17
17
  let(:app) do
18
18
  fuzz = defined?(TRAVIS_BUILD_ID) ? TRAVIS_BUILD_ID : Time.new.to_f.to_s.gsub(".", "_")
@@ -30,15 +30,6 @@ if ENV['VMC_TEST_USER'] && ENV['VMC_TEST_PASSWORD'] && ENV['VMC_TEST_TARGET']
30
30
  Interact::Progress::Dots.stop!
31
31
  end
32
32
 
33
- let(:vmc_bin) do
34
- vmc = File.expand_path("#{SPEC_ROOT}/../bin/vmc.dev")
35
- if INTEGRATE_WITH != 'default'
36
- "rvm #{INTEGRATE_WITH}@vmc do #{vmc}"
37
- else
38
- vmc
39
- end
40
- end
41
-
42
33
  it 'pushes a simple sinatra app using defaults as much as possible' do
43
34
  run("#{vmc_bin} target http://#{target}") do |runner|
44
35
  expect(runner).to say %r{Setting target to http://#{target}... OK}
@@ -98,7 +89,10 @@ if ENV['VMC_TEST_USER'] && ENV['VMC_TEST_PASSWORD'] && ENV['VMC_TEST_TARGET']
98
89
 
99
90
  expect(runner).to say "Uploading #{app}... OK"
100
91
  expect(runner).to say "Starting #{app}... OK"
101
- expect(runner).to say "Checking #{app}... OK", 30
92
+
93
+ expect(runner).to say "Checking #{app}..."
94
+ expect(runner).to say "1/1 instances"
95
+ expect(runner).to say "OK", 30
102
96
  end
103
97
  end
104
98
 
@@ -111,5 +105,5 @@ if ENV['VMC_TEST_USER'] && ENV['VMC_TEST_PASSWORD'] && ENV['VMC_TEST_TARGET']
111
105
  end
112
106
  end
113
107
  else
114
- $stderr.puts 'Skipping integration specs; please provide $VMC_TEST_TARGET, $VMC_TEST_USER, and $VMC_TEST_PASSWORD'
115
- end
108
+ $stderr.puts 'Skipping v1 integration specs; please provide $VMC_TEST_TARGET, $VMC_TEST_USER, and $VMC_TEST_PASSWORD'
109
+ end
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+ require "webmock/rspec"
3
+
4
+ if ENV['VMC_V2_TEST_USER'] && ENV['VMC_V2_TEST_PASSWORD'] && ENV['VMC_V2_TEST_TARGET']
5
+ describe 'A new user tries to use VMC against v2 production', :ruby19 => true do
6
+ include ConsoleAppSpeckerMatchers
7
+
8
+ let(:target) { ENV['VMC_V2_TEST_TARGET'] }
9
+ let(:username) { ENV['VMC_V2_TEST_USER'] }
10
+ let(:password) { ENV['VMC_V2_TEST_PASSWORD'] }
11
+
12
+ before do
13
+ Interact::Progress::Dots.start!
14
+ end
15
+
16
+ after do
17
+ Interact::Progress::Dots.stop!
18
+ end
19
+
20
+ it "registers a new account and deletes it" do
21
+ pending "until we get some v2 admin credentials somewhere to actually run this with"
22
+
23
+ email = Faker::Internet.email
24
+ run("#{vmc_bin} target #{target}") do |runner|
25
+ runner.wait_for_exit
26
+ end
27
+
28
+ run("#{vmc_bin} login #{username} --password #{password}") do |runner|
29
+ expect(runner).to say "Organization>"
30
+ runner.send_keys "1"
31
+ expect(runner).to say "Space>"
32
+ runner.send_keys "1"
33
+ end
34
+
35
+ puts "registering #{email}"
36
+ run("#{vmc_bin} register #{email} --password p") do |runner|
37
+ expect(runner).to say "Confirm Password>"
38
+ runner.send_keys 'p'
39
+ expect(runner).to say "Your password strength is: good"
40
+ expect(runner).to say "Creating user... OK"
41
+ expect(runner).to say "Authenticating... OK"
42
+ end
43
+
44
+ run("#{vmc_bin} logout") do |runner|
45
+ runner.wait_for_exit
46
+ end
47
+
48
+ run("#{vmc_bin} login #{username} --password #{password}") do |runner|
49
+ expect(runner).to say "Organization>"
50
+ runner.send_keys "1"
51
+ expect(runner).to say "Space>"
52
+ runner.send_keys "1"
53
+ end
54
+
55
+ run("#{vmc_bin} delete-user #{email}") do |runner|
56
+ expect(runner).to say "Really delete user #{email}?>"
57
+ runner.send_keys "y"
58
+ expect(runner).to say "Deleting #{email}... OK"
59
+ end
60
+ end
61
+ end
62
+ else
63
+ $stderr.puts 'Skipping v2 integration specs; please provide $VMC_V2_TEST_TARGET, $VMC_V2_TEST_USER, and $VMC_V2_TEST_PASSWORD'
64
+ end
@@ -0,0 +1,126 @@
1
+ require "spec_helper"
2
+ require "webmock/rspec"
3
+
4
+ if ENV['VMC_V2_TEST_USER'] && ENV['VMC_V2_TEST_PASSWORD'] && ENV['VMC_V2_TEST_TARGET']
5
+ describe 'A new user tries to use VMC against v2', :ruby19 => true do
6
+ include ConsoleAppSpeckerMatchers
7
+ include VMC::Interactive
8
+
9
+ let(:target) { ENV['VMC_V2_TEST_TARGET'] }
10
+ let(:username) { ENV['VMC_V2_TEST_USER'] }
11
+ let(:password) { ENV['VMC_V2_TEST_PASSWORD'] }
12
+
13
+ let(:app) do
14
+ fuzz = defined?(TRAVIS_BUILD_ID) ? TRAVIS_BUILD_ID : Time.new.to_f.to_s.gsub(".", "_")
15
+ "hello-sinatra-#{fuzz}"
16
+ end
17
+
18
+ before do
19
+ FileUtils.rm_rf File.expand_path(VMC::CONFIG_DIR)
20
+ WebMock.allow_net_connect!
21
+ Interact::Progress::Dots.start!
22
+ end
23
+
24
+ after do
25
+ vmc %W(delete #{app} -f --no-script)
26
+ Interact::Progress::Dots.stop!
27
+ end
28
+
29
+ it 'pushes a simple sinatra app using defaults as much as possible' do
30
+ run("#{vmc_bin} target http://#{target}") do |runner|
31
+ expect(runner).to say %r{Setting target to http://#{target}... OK}
32
+ end
33
+
34
+ run("#{vmc_bin} login") do |runner|
35
+ expect(runner).to say %r{target: https?://#{target}}
36
+
37
+ expect(runner).to say "Email>"
38
+ runner.send_keys username
39
+
40
+ expect(runner).to say "Password>"
41
+ runner.send_keys password
42
+
43
+ expect(runner).to say "Authenticating... OK"
44
+
45
+ expect(runner).to say "1:"
46
+ expect(runner).to say "Organization>"
47
+ runner.send_keys "1"
48
+
49
+ expect(runner).to say /Switching to organization .*\.\.\. OK/
50
+ expect(runner).to say /Switching to space .*\.\.\. OK/
51
+ end
52
+
53
+ run("#{vmc_bin} app #{app}") do |runner|
54
+ expect(runner).to say "Unknown app '#{app}'."
55
+ end
56
+
57
+ Dir.chdir("#{SPEC_ROOT}/assets/hello-sinatra") do
58
+ run("#{vmc_bin} push") do |runner|
59
+ expect(runner).to say "Name>"
60
+ runner.send_keys app
61
+
62
+ expect(runner).to say "Instances> 1"
63
+ runner.send_keys ""
64
+
65
+ expect(runner).to say ": other"
66
+ expect(runner).to say "Framework>"
67
+ runner.send_keys "other"
68
+
69
+ expect(runner).to say ": buildpack"
70
+ expect(runner).to say "Framework>"
71
+ runner.send_keys "buildpack"
72
+
73
+ expect(runner).to say "Use custom startup command?> "
74
+ runner.send_keys "y"
75
+
76
+ expect(runner).to say "Startup command> "
77
+ runner.send_keys "bundle exec ruby main.rb -p $PORT"
78
+
79
+ expect(runner).to say ": ruby19"
80
+ expect(runner).to say "Runtime>"
81
+ runner.send_keys "ruby19"
82
+
83
+ expect(runner).to say "Memory Limit>"
84
+ runner.send_keys "64M"
85
+
86
+ expect(runner).to say "Creating #{app}... OK"
87
+
88
+ expect(runner).to say "Subdomain> #{app}"
89
+ runner.send_keys ""
90
+
91
+ expect(runner).to say "1:"
92
+ expect(runner).to say "Domain>"
93
+ runner.send_keys "1"
94
+
95
+ expect(runner).to say(/Creating route #{app}\..*\.\.\. OK/)
96
+ expect(runner).to say(/Binding #{app}\..* to #{app}\.\.\. OK/)
97
+
98
+ expect(runner).to say "Create services for application?> n"
99
+ runner.send_keys ""
100
+
101
+ expect(runner).to say "Save configuration?> n"
102
+ runner.send_keys ""
103
+
104
+ expect(runner).to say "Uploading #{app}... OK"
105
+ expect(runner).to say "Starting #{app}... OK"
106
+
107
+ expect(runner).to say "Using Ruby", 10
108
+ expect(runner).to say "Your bundle is complete!", 30
109
+
110
+ expect(runner).to say "Checking #{app}..."
111
+ expect(runner).to say "1/1 instances"
112
+ expect(runner).to say "OK", 30
113
+ end
114
+ end
115
+
116
+ run("#{vmc_bin} delete #{app}") do |runner|
117
+ expect(runner).to say "Really delete #{app}?>"
118
+ runner.send_keys "y"
119
+
120
+ expect(runner).to say "Deleting #{app}... OK"
121
+ end
122
+ end
123
+ end
124
+ else
125
+ $stderr.puts 'Skipping integration specs; please provide $VMC_TEST_TARGET, $VMC_TEST_USER, and $VMC_TEST_PASSWORD'
126
+ end