vmc 0.5.0.rc4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
1
  require "rake"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
5
- require "vmc/version"
4
+ specfile, _ = Dir["*.gemspec"]
5
+ SPEC = Gem::Specification.load(specfile)
6
+ CURRENT_VERSION = SPEC.version.to_s.freeze
6
7
 
7
8
  RSpec::Core::RakeTask.new(:spec)
8
9
  task :default => :spec
@@ -20,20 +21,81 @@ namespace :deploy do
20
21
  last_staging_sha == last_release_sha
21
22
  end
22
23
 
24
+ def next_minor
25
+ Gem::Version.new(CURRENT_VERSION + ".0").bump.to_s
26
+ end
27
+
28
+ def next_rc
29
+ unless CURRENT_VERSION =~ /rc/
30
+ "#{next_minor}.rc1"
31
+ end
32
+ end
33
+
23
34
  task :staging, :version do |_, args|
24
- sh "gem bump --push #{"--version #{args.version}" if args.version}" if last_staging_ref_was_released?
35
+ version = args.version || next_rc
36
+ sh "gem bump --push #{"--version #{version}" if version}" if last_staging_ref_was_released?
25
37
  sh "git tag -f latest-staging"
26
38
  sh "git push origin :latest-staging"
27
39
  sh "git push origin latest-staging"
28
40
  end
29
41
 
30
- task :gem do
42
+ task :candidate do
31
43
  sh "git fetch"
32
- sh "git checkout #{last_staging_sha}"
44
+ sh "git checkout latest-staging"
33
45
  sh "gem release"
34
- sh "git tag -f v#{VMC::VERSION}"
46
+ sh "git tag -f v#{CURRENT_VERSION}"
35
47
  sh "git tag -f latest-release"
36
48
  sh "git push origin :latest-release"
37
49
  sh "git push origin latest-release"
38
50
  end
51
+
52
+ task :release do
53
+ version = CURRENT_VERSION.sub(/\.rc\d+/, "")
54
+
55
+ prereleases = SPEC.runtime_dependencies.select(&:prerelease?)
56
+ unless prereleases.empty?
57
+ puts "The following dependencies must be released:"
58
+ prereleases.each do |pre|
59
+ puts "- #{pre.name}"
60
+ end
61
+
62
+ puts ""
63
+
64
+ raise
65
+ end
66
+
67
+ # grab the last release candidate
68
+ sh "git checkout latest-release -b release-v#{version}"
69
+
70
+ # update the version
71
+ sh "gem bump --version #{version}"
72
+
73
+ # switch to master
74
+ sh "git checkout master"
75
+
76
+ # merge the new version number back into master
77
+ sh "git merge release-v#{version}"
78
+
79
+ # apply tags
80
+ sh "git tag -f v#{version}"
81
+ sh "git tag -f latest-release"
82
+ sh "git tag -f latest-stable"
83
+ sh "git push origin master"
84
+ sh "git push origin :latest-release 2> /dev/null || exit 0"
85
+ sh "git push origin :latest-stable 2> /dev/null || exit 0"
86
+ sh "git push origin latest-release"
87
+ sh "git push origin latest-stable"
88
+ sh "git push origin v#{version}"
89
+
90
+ # check out the release tag
91
+ sh "git checkout latest-stable"
92
+
93
+ # build the gem and push the gem to rubygems
94
+ sh "rm -f *.gem"
95
+ sh "gem build *.gemspec"
96
+ sh "gem push *.gem"
97
+ sh "rm -f *.gem"
98
+
99
+ puts "You are now on the latest-release tag. You'll have to switch back to your working branch."
100
+ end
39
101
  end
@@ -22,7 +22,14 @@ module VMC::Start
22
22
  if input.has?(:url)
23
23
  target = sane_target_url(input[:url])
24
24
  with_progress("Setting target to #{c(target, :name)}") do
25
- CFoundry::Client.new(target) # check that it's valid before setting
25
+ begin
26
+ CFoundry::Client.new(target) # check that it's valid before setting
27
+ rescue CFoundry::TargetRefused
28
+ fail "Target refused connection."
29
+ rescue CFoundry::InvalidTarget
30
+ fail "Invalid target URI."
31
+ end
32
+
26
33
  set_target(target)
27
34
  end
28
35
  end
data/lib/vmc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module VMC
2
- VERSION = "0.5.0.rc4".freeze
2
+ VERSION = "0.5.0".freeze
3
3
  end
@@ -4,12 +4,8 @@ require 'ffaker'
4
4
 
5
5
  if ENV['VMC_TEST_USER'] && ENV['VMC_TEST_PASSWORD'] && ENV['VMC_TEST_TARGET']
6
6
  describe 'A new user tries to use VMC against v1 production', :ruby19 => true do
7
- include ConsoleAppSpeckerMatchers
8
7
  include VMC::Interactive
9
8
 
10
- let(:output) { StringIO.new }
11
- let(:out) { output.string.strip_progress_dots }
12
-
13
9
  let(:target) { ENV['VMC_TEST_TARGET'] }
14
10
  let(:username) { ENV['VMC_TEST_USER'] }
15
11
  let(:password) { ENV['VMC_TEST_PASSWORD'] }
data/spec/spec_helper.rb CHANGED
@@ -26,6 +26,8 @@ end
26
26
  RSpec.configure do |c|
27
27
  c.include Fake::FakeMethods
28
28
  c.include V1Fake::FakeMethods
29
+ c.include ConsoleAppSpeckerMatchers
30
+
29
31
  c.mock_with :rr
30
32
 
31
33
  if RUBY_VERSION =~ /^1\.8\.\d/
@@ -46,18 +48,6 @@ RSpec.configure do |c|
46
48
  end
47
49
  end
48
50
 
49
- class String
50
- def strip_heredoc
51
- min = scan(/^[ \t]*(?=\S)/).min
52
- indent = min ? min.size : 0
53
- gsub(/^[ \t]{#{indent}}/, '')
54
- end
55
-
56
- def strip_progress_dots
57
- gsub(/\. \x08([\x08\. ]+)/, "... ")
58
- end
59
- end
60
-
61
51
  def name_list(xs)
62
52
  if xs.empty?
63
53
  "none"
@@ -66,19 +56,6 @@ def name_list(xs)
66
56
  end
67
57
  end
68
58
 
69
- def invoke_cli(cli, *args)
70
- stub.proxy(cli).invoke.with_any_args
71
- stub(cli.class).new { cli }
72
- cli.invoke(*args)
73
- end
74
-
75
- def stub_output(cli)
76
- stub(cli).print
77
- stub(cli).puts
78
- stub(Interact::Progress::Dots).start!
79
- stub(Interact::Progress::Dots).stop!
80
- end
81
-
82
59
  def run(command)
83
60
  SpeckerRunner.new(command) do |runner|
84
61
  yield runner
@@ -1,32 +1,80 @@
1
+ def command(klass, &specs)
2
+ describe klass do
3
+ let(:stub_precondition?) { true }
4
+ before do
5
+ any_instance_of klass do |cli|
6
+ stub(cli).precondition if stub_precondition?
7
+ stub(cli).client { client }
8
+ end
9
+ end
10
+
11
+ before(:all) do
12
+ klass.class_eval do
13
+ def wrap_errors
14
+ yield
15
+ rescue VMC::UserError => e
16
+ err e.message
17
+ end
18
+ end
19
+ end
20
+
21
+ after(:all) do
22
+ klass.class_eval do
23
+ remove_method :wrap_errors
24
+ end
25
+ end
26
+
27
+ class_eval(&specs)
28
+ end
29
+ end
30
+
1
31
  module CommandHelper
2
32
  def vmc(argv)
3
33
  Mothership.new.exit_status 0
4
34
  stub(VMC::CLI).exit { |code| code }
5
- capture_output { VMC::CLI.start(argv + ["--debug"]) }
6
- end
7
-
8
- def expect_status_and_output(status = 0, out = "", err = "")
9
- expect([
10
- status,
11
- stdout.string.strip_progress_dots,
12
- stderr.string.strip_progress_dots
13
- ]).to eq([status, out, err])
35
+ capture_output { VMC::CLI.start(argv + ["--debug", "--no-script"]) }
14
36
  end
15
37
 
16
38
  def bool_flag(flag)
17
39
  "#{'no-' unless send(flag)}#{flag.to_s.gsub('_', '-')}"
18
40
  end
19
41
 
20
- attr_reader :stdout, :stderr, :status
42
+ attr_reader :stdout, :stderr, :stdin, :status
21
43
 
22
44
  def capture_output
23
45
  $real_stdout = $stdout
24
46
  $real_stderr = $stderr
47
+ $real_stdin = $stdin
25
48
  $stdout = @stdout = StringIO.new
26
49
  $stderr = @stderr = StringIO.new
50
+ $stdin = @stdin = StringIO.new
27
51
  @status = yield
52
+ @stdout.rewind
53
+ @stderr.rewind
54
+ @status
28
55
  ensure
29
56
  $stdout = $real_stdout
30
57
  $stderr = $real_stderr
58
+ $stdin = $real_stdin
59
+ end
60
+
61
+ def output
62
+ @output ||= TrackingExpector.new(stdout)
63
+ end
64
+
65
+ def error_output
66
+ @error_output ||= TrackingExpector.new(stderr)
67
+ end
68
+
69
+ def mock_invoke(*args)
70
+ any_instance_of described_class do |cli|
71
+ mock(cli).invoke *args
72
+ end
73
+ end
74
+
75
+ def dont_allow_invoke(*args)
76
+ any_instance_of described_class do |cli|
77
+ dont_allow(cli).invoke *args
78
+ end
31
79
  end
32
80
  end
@@ -1,3 +1,5 @@
1
+ require "fakefs/safe"
2
+
1
3
  module FakeHomeDir
2
4
  def self.included(klass)
3
5
  super
@@ -5,38 +7,49 @@ module FakeHomeDir
5
7
  end
6
8
 
7
9
  module ClassMethods
8
- def use_fake_home_dir(&block)
10
+ def stub_home_dir_with(&block)
9
11
  around do |example|
10
- dir = instance_exec(&block)
11
- with_fake_home_dir(dir) do
12
+ bootstrap_dir = instance_exec(&block)
13
+
14
+ if bootstrap_dir
15
+ fixture = File.expand_path(bootstrap_dir)
16
+ files = build_file_buffer(fixture)
17
+ end
18
+
19
+ FakeFS do
20
+ home = File.expand_path("~")
21
+ write_file_buffer(files, home) if files
12
22
  example.call
13
23
  end
24
+
25
+ FakeFS::FileSystem.clear
14
26
  end
15
27
  end
28
+ end
16
29
 
17
- def stub_home_dir_with(folder_name)
18
- around do |example|
19
- tmp_root = Dir.tmpdir
20
- FileUtils.cp_r(File.expand_path("#{SPEC_ROOT}/fixtures/fake_home_dirs/#{folder_name}"), tmp_root)
21
- fake_home_dir = "#{tmp_root}/#{folder_name}"
22
- begin
23
- with_fake_home_dir(fake_home_dir) do
24
- example.call
25
- end
26
- ensure
27
- FileUtils.rm_rf fake_home_dir
28
- end
29
- end
30
+ private
31
+
32
+ def build_file_buffer(path)
33
+ files = {}
34
+
35
+ Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).each do |file|
36
+ next if file =~ /\.$/
37
+ next if File.directory?(file)
38
+
39
+ files[file.sub(path + "/", "")] = File.read(file)
30
40
  end
41
+
42
+ files
31
43
  end
32
44
 
33
- def with_fake_home_dir(dir, &block)
34
- original_home_dir = ENV['HOME']
35
- ENV['HOME'] = dir
36
- begin
37
- block.call
38
- ensure
39
- ENV['HOME'] = original_home_dir
45
+ def write_file_buffer(files, path)
46
+ files.each do |file, body|
47
+ full = "#{path}/#{file}"
48
+
49
+ FileUtils.mkdir_p(File.dirname(full))
50
+ File.open(full, "w") do |io|
51
+ io.write body
52
+ end
40
53
  end
41
54
  end
42
55
  end
@@ -50,16 +50,16 @@ describe VMC::App::Stats do
50
50
 
51
51
  it 'prints out the instances in the correct order' do
52
52
  subject
53
- expect(stdout.string).to match /.*instance \#1.*instance \#2.*instance \#12.*/m
53
+ expect(output).to say("instance #1")
54
+ expect(output).to say("instance #2")
55
+ expect(output).to say("instance #12")
54
56
  end
55
57
 
56
58
  it 'prints out one of the instances correctly' do
57
59
  subject
58
- expect(stdout.string).to include <<-OUT.strip_heredoc
59
- instance #2: started
60
- started: #{time.strftime("%F %r")}
61
- debugger: port bar at foo
62
- console: port qux at baz
63
- OUT
60
+ expect(output).to say("instance #2: started")
61
+ expect(output).to say(" started: #{time.strftime("%F %r")}")
62
+ expect(output).to say(" debugger: port bar at foo")
63
+ expect(output).to say(" console: port qux at baz")
64
64
  end
65
65
  end
@@ -1,23 +1,14 @@
1
1
  require "spec_helper"
2
2
  require "webmock/rspec"
3
3
 
4
- describe VMC::App::Scale do
5
- let(:global) { { :color => false } }
6
- let(:given) { {} }
7
- let(:client) { fake_client }
8
- let!(:cli) { described_class.new }
9
-
10
- before do
11
- stub(cli).client { client }
12
- stub_output(cli)
13
- end
14
-
15
- subject { invoke_cli(cli, :scale, inputs, given, global) }
4
+ command VMC::App::Scale do
5
+ let(:client) { fake_client :apps => [app] }
16
6
 
17
7
  context "when the --disk flag is given" do
18
8
  let(:before_value) { 512 }
19
9
  let(:app) { fake :app, :disk_quota => before_value }
20
- let(:inputs) { { :app => app, :disk => "1G" } }
10
+
11
+ subject { vmc %W[scale #{app.name} --disk 1G] }
21
12
 
22
13
  it "changes the application's disk quota" do
23
14
  mock(app).update!
@@ -28,18 +19,20 @@ describe VMC::App::Scale do
28
19
  context "when the --memory flag is given" do
29
20
  let(:before_value) { 512 }
30
21
  let(:app) { fake :app, :memory => before_value }
31
- let(:inputs) { { :app => app, :memory => "1G" } }
22
+
23
+ subject { vmc %W[scale #{app.name} --memory 1G] }
32
24
 
33
25
  it "changes the application's memory" do
34
26
  mock(app).update!
35
27
  expect { subject }.to change(app, :memory).from(before_value).to(1024)
36
28
  end
37
29
 
30
+ # TODO: determine if the command should do this on v2
38
31
  context "if --restart is true" do
39
32
  it "restarts the application" do
40
33
  stub(app).update!
41
34
  stub(app).started? { true }
42
- mock(cli).invoke :restart, :app => app
35
+ mock_invoke :restart, :app => app
43
36
  subject
44
37
  end
45
38
  end
@@ -49,7 +42,7 @@ describe VMC::App::Scale do
49
42
  let(:before_value) { 3 }
50
43
  let(:app) { fake :app, :total_instances => before_value }
51
44
 
52
- let(:inputs) { { :app => app, :instances => 5 } }
45
+ subject { vmc %W[scale #{app.name} --instances 5] }
53
46
 
54
47
  it "changes the application's number of instances" do
55
48
  mock(app).update!
@@ -60,7 +53,8 @@ describe VMC::App::Scale do
60
53
  context "when the --plan flag is given" do
61
54
  context "when the plan name begins with a 'p'" do
62
55
  let(:app) { fake :app, :production => false }
63
- let(:inputs) { { :app => app, :plan => "P100" } }
56
+
57
+ subject { vmc %W[scale #{app.name} --plan P100] }
64
58
 
65
59
  it "changes the application's 'production' flag to true" do
66
60
  mock(app).update!
@@ -70,7 +64,8 @@ describe VMC::App::Scale do
70
64
 
71
65
  context "when the plan name does not begin with a 'p'" do
72
66
  let(:app) { fake :app, :production => true }
73
- let(:inputs) { { :app => app, :plan => "D100" } }
67
+
68
+ subject { vmc %W[scale #{app.name} --plan D100] }
74
69
 
75
70
  it "changes the application's 'production' flag to false" do
76
71
  mock(app).update!
@@ -1,44 +1,11 @@
1
1
  require "spec_helper"
2
2
  require "webmock/rspec"
3
3
 
4
- describe VMC::App::Start do
5
- include ConsoleAppSpeckerMatchers
6
-
4
+ command VMC::App::Start do
7
5
  let(:client) { fake_client :apps => [app] }
8
6
  let(:app) { fake :app }
9
7
 
10
- def output
11
- stdout.rewind
12
- TrackingExpector.new(stdout)
13
- end
14
-
15
- def error_output
16
- stderr.rewind
17
- TrackingExpector.new(stderr)
18
- end
19
-
20
- before do
21
- any_instance_of described_class do |cli|
22
- stub(cli).precondition
23
- stub(cli).client { client }
24
- end
25
- end
26
-
27
- before(:all) do
28
- described_class.class_eval do
29
- def wrap_errors
30
- yield
31
- end
32
- end
33
- end
34
-
35
- after(:all) do
36
- described_class.class_eval do
37
- remove_method :wrap_errors
38
- end
39
- end
40
-
41
- subject { vmc %W[start #{app.name} --no-quiet] }
8
+ subject { vmc %W[start #{app.name}] }
42
9
 
43
10
  context "with an app that's already started" do
44
11
  let(:app) { fake :app, :state => "STARTED" }
@@ -152,14 +119,14 @@ describe VMC::App::Start do
152
119
  stub_request(:get, "#{log_url}&tail&tail_offset=0").to_return(
153
120
  :status => 404, :body => "")
154
121
  end
155
-
122
+
156
123
  it_says_application_is_starting
157
124
  it_does_not_print_log_progress
158
125
  it_waits_for_application_to_become_healthy
159
126
  end
160
127
 
161
128
  context "and progress log url becomes unavailable after some time" do
162
- before do
129
+ before do
163
130
  stub_request(:get, "#{log_url}&tail&tail_offset=0").to_return(
164
131
  :status => 200, :body => log_text[0...5])
165
132
  stub_request(:get, "#{log_url}&tail&tail_offset=5").to_return(
@@ -174,7 +141,7 @@ describe VMC::App::Start do
174
141
  end
175
142
 
176
143
  context "and a request times out" do
177
- before do
144
+ before do
178
145
  stub_request(:get, "#{log_url}&tail&tail_offset=0").to_return(
179
146
  :should_timeout => true)
180
147
  stub_request(:get, "#{log_url}&tail&tail_offset=0").to_return(
@@ -198,4 +165,4 @@ describe VMC::App::Start do
198
165
  it_waits_for_application_to_become_healthy
199
166
  end
200
167
  end
201
- end
168
+ end
@@ -1,24 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe VMC::Domain::Map do
4
- let(:global) { { :color => false } }
5
- let(:given) { {} }
6
- let(:client) { fake_client :current_organization => organization, :current_space => space }
7
- let!(:cli) { described_class.new }
8
-
9
- before do
10
- stub(cli).client { client }
11
- stub_output(cli)
3
+ command VMC::Domain::Map do
4
+ let(:client) do
5
+ fake_client(
6
+ :current_organization => organization,
7
+ :current_space => space,
8
+ :spaces => [space],
9
+ :organizations => [organization],
10
+ :domains => domains)
12
11
  end
13
12
 
14
13
  let(:organization) { fake(:organization) }
15
- let(:space) { fake(:space) }
14
+ let(:space) { fake(:space, :organization => organization) }
16
15
  let(:domain) { fake(:domain, :name => domain_name) }
17
16
  let(:domain_name) { "some.domain.com" }
17
+ let(:domains) { [domain] }
18
18
 
19
- subject { invoke_cli(cli, :map_domain, inputs, given, global) }
20
-
21
- shared_examples "binding a domain to a space" do
19
+ shared_examples_for "binding a domain to a space" do
22
20
  it "adds the domain to the space's organization" do
23
21
  mock(space.organization).add_domain(domain)
24
22
  stub(space).add_domain(domain)
@@ -32,7 +30,7 @@ describe VMC::Domain::Map do
32
30
  end
33
31
  end
34
32
 
35
- shared_examples "binding a domain to an organization" do
33
+ shared_examples_for "binding a domain to an organization" do
36
34
  it 'does NOT add the domain to a space' do
37
35
  any_instance_of(space.class) do |space|
38
36
  dont_allow(space).add_domain(domain)
@@ -45,8 +43,10 @@ describe VMC::Domain::Map do
45
43
  end
46
44
  end
47
45
 
48
- shared_examples "mapping a domain to a space" do
46
+ shared_examples_for "mapping a domain to a space" do
49
47
  context "when the domain does NOT exist" do
48
+ let(:domains) { [] }
49
+
50
50
  before do
51
51
  stub(client).domain { domain }
52
52
  stub(domain).create!
@@ -65,26 +65,22 @@ describe VMC::Domain::Map do
65
65
  end
66
66
 
67
67
  context "when the domain already exists" do
68
- let(:client) {
69
- fake_client :domains => [domain],
70
- :current_organization => organization,
71
- :current_space => space
72
- }
73
-
74
68
  include_examples "binding a domain to a space"
75
69
  end
76
70
  end
77
71
 
78
72
  context 'when a domain and a space are passed' do
79
- let(:inputs) { { :space => space, :name => domain_name } }
73
+ subject { vmc %W[map-domain #{domain.name} --space #{space.name}] }
80
74
 
81
75
  include_examples "mapping a domain to a space"
82
76
  end
83
77
 
84
78
  context 'when a domain and an organization are passed' do
85
- let(:inputs) { { :organization => organization, :name => domain_name } }
79
+ subject { vmc %W[map-domain #{domain.name} --organization #{organization.name}] }
86
80
 
87
81
  context "and the domain does NOT exist" do
82
+ let(:domains) { [] }
83
+
88
84
  before do
89
85
  stub(client).domain { domain }
90
86
  stub(domain).create!
@@ -93,18 +89,18 @@ describe VMC::Domain::Map do
93
89
 
94
90
  include_examples "binding a domain to an organization"
95
91
 
96
- it 'adds the domain to the organization' do
97
- mock(organization).add_domain(domain)
92
+ it 'creates the domain' do
93
+ mock(domain).create!
98
94
  subject
95
+ expect(domain.name).to eq domain_name
99
96
  end
100
97
 
101
98
  context "and the --shared option is passed" do
102
- let(:inputs) { { :organization => organization, :name => domain_name, :shared => true } }
99
+ subject { vmc %W[map-domain #{domain.name} --organization #{organization.name} --shared] }
103
100
 
104
101
  it 'adds the domain to the organization' do
105
- mock(domain).create!
102
+ mock(organization).add_domain(domain)
106
103
  subject
107
- expect(domain.name).to eq domain_name
108
104
  end
109
105
 
110
106
  it "does not add the domain to a specific organization" do
@@ -116,25 +112,19 @@ describe VMC::Domain::Map do
116
112
  end
117
113
 
118
114
  context "and the domain already exists" do
119
- let(:client) {
120
- fake_client :domains => [domain],
121
- :current_organization => organization,
122
- :current_space => space
123
- }
124
-
125
115
  include_examples "binding a domain to an organization"
126
116
  end
127
117
  end
128
118
 
129
119
  context 'when a domain, organization, and space is passed' do
130
- let(:inputs) { { :name => domain_name, :organization => organization, :space => space } }
120
+ subject { vmc %W[map-domain #{domain.name} --space #{space.name} --organization #{organization.name}] }
131
121
 
132
122
  include_examples "mapping a domain to a space"
133
123
  end
134
124
 
135
125
  context 'when only a domain is passed' do
136
- let(:inputs) { { :name => domain_name } }
126
+ subject { vmc %W[map-domain #{domain.name}] }
137
127
 
138
128
  include_examples "mapping a domain to a space"
139
129
  end
140
- end
130
+ end