vmc 0.5.0.rc4 → 0.5.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/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