cf 5.2.1.rc4 → 5.2.1.rc5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/admin/plugin.rb CHANGED
@@ -5,3 +5,4 @@ require "admin/service_auth_token"
5
5
  require "admin/service_broker/add"
6
6
  require "admin/service_broker/remove"
7
7
  require "admin/service_broker/service_brokers"
8
+ require "admin/service_broker/update"
@@ -0,0 +1,52 @@
1
+ require "cf/cli"
2
+
3
+ module CFAdmin::ServiceBroker
4
+ class Update < CF::CLI
5
+ def precondition
6
+ check_target
7
+ end
8
+
9
+ desc "Update a service broker"
10
+ group :admin
11
+ input :broker, :argument => :required,
12
+ :desc => "Service broker to update",
13
+ :from_given => by_name(:service_broker)
14
+ input :name, :argument => :optional,
15
+ :desc => "New name"
16
+ input :url,
17
+ :desc => "New URL"
18
+ input :token,
19
+ :desc => "New token"
20
+
21
+ def update_service_broker
22
+ broker = input[:broker]
23
+
24
+ old_name = broker.name
25
+
26
+ broker.name = input[:name]
27
+ finalize
28
+ broker.broker_url = input[:url]
29
+ finalize
30
+ broker.token = input[:token]
31
+ finalize
32
+
33
+ with_progress("Updating service broker #{old_name}") do
34
+ broker.update!
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def ask_name
41
+ ask("Name")
42
+ end
43
+
44
+ def ask_url
45
+ ask("URL")
46
+ end
47
+
48
+ def ask_token
49
+ ask("Token")
50
+ end
51
+ end
52
+ end
data/lib/cf/cli.rb CHANGED
@@ -158,12 +158,18 @@ EOS
158
158
  formatted_exception_output(e, message)
159
159
 
160
160
  rescue Exception => e
161
- formatted_exception_output(e, add_exception_name_to_msg(e))
161
+ log_error_and_dump_crashlog(e)
162
+ end
163
+
164
+ def log_error_and_dump_crashlog(e)
165
+ log_error(e)
166
+ err File.open(File.expand_path(CF::CRASH_FILE)).readlines.join("")
167
+
168
+ raise if debug?
162
169
  end
163
170
 
164
171
  def formatted_exception_output(e, msg)
165
172
  log_error(e)
166
- msg << "\ncat #{CF::CRASH_FILE} # for more details"
167
173
  err msg
168
174
 
169
175
  raise if debug?
@@ -29,50 +29,71 @@ module CF::App
29
29
  :argument => true, :from_given => by_name(:app)
30
30
  input :name, :desc => "Variable name", :argument => true
31
31
  input :value, :desc => "Variable value", :argument => :optional
32
- input :restart, :desc => "Restart app after updating?", :default => true
32
+ input :restart, :desc => "Restart app after updating?", :default => false
33
+
33
34
  def set_env
34
35
  app = input[:app]
35
- name = input[:name]
36
-
37
- if value = input[:value]
38
- name = input[:name]
39
- elsif name["="]
40
- name, value = name.split("=")
41
- end
42
-
43
- unless name =~ VALID_ENV_VAR
44
- fail "Invalid variable name; must match #{VALID_ENV_VAR.inspect}"
45
- end
36
+ name, value = parse_name_and_value!
46
37
 
47
- with_progress("Updating #{c(app.name, :name)}") do
38
+ with_progress("Updating env variable #{c(name, :name)} for app #{c(app.name, :name)}") do
48
39
  app.env[name] = value
49
40
  app.update!
50
41
  end
51
42
 
52
- if app.started? && input[:restart]
53
- invoke :restart, :app => app
54
- end
43
+ restart_if_necessary(app)
55
44
  end
56
45
 
57
-
58
46
  desc "Remove an environment variable"
59
47
  group :apps, :info
60
48
  input :app, :desc => "Application to set the variable for",
61
49
  :argument => true, :from_given => by_name(:app)
62
50
  input :name, :desc => "Variable name", :argument => true
63
- input :restart, :desc => "Restart app after updating?", :default => true
51
+ input :restart, :desc => "Restart app after updating?", :default => false
64
52
  def unset_env
65
53
  app = input[:app]
66
54
  name = input[:name]
67
55
 
68
- with_progress("Updating #{c(app.name, :name)}") do
56
+ with_progress("Unsetting #{c(name, :name)} for app #{c(app.name, :name)}") do
69
57
  app.env.delete(name)
70
58
  app.update!
71
59
  end
72
60
 
73
- if app.started? && input[:restart]
61
+ restart_if_necessary(app)
62
+ end
63
+
64
+ private
65
+
66
+ def restart_if_necessary(app)
67
+ unless input[:restart]
68
+ line c("TIP: Use 'cf push' to ensure your env variable changes take effect.", :warning)
69
+ return
70
+ end
71
+
72
+ if app.started?
74
73
  invoke :restart, :app => app
74
+ else
75
+ line "Your app was unstarted. Starting now."
76
+ invoke :start, :app => app
77
+ end
78
+ end
79
+
80
+ def parse_name_and_value!
81
+ name = input[:name]
82
+ value = input[:value]
83
+
84
+ if name["="]
85
+ name, new_value, extra_values = name.split("=")
86
+ fail "You attempted to specify the value of #{name} twice." if value
87
+ fail "Invalid format: environment variable definition contains too many occurences of '='" if extra_values
88
+ value = new_value
89
+ end
90
+
91
+ unless name =~ VALID_ENV_VAR
92
+ fail "Invalid format: environment variable names cannot start with a number" if name[0] =~ /\d/
93
+ fail "Invalid format: environment variable names can only contain alphanumeric characters and underscores"
75
94
  end
95
+
96
+ return name, value
76
97
  end
77
98
  end
78
99
  end
@@ -49,15 +49,6 @@ module CF::App
49
49
  end
50
50
  end
51
51
 
52
- def sync_app(app, path)
53
- upload_app(app, path)
54
- apply_changes(app)
55
- display_changes(app)
56
- commit_changes(app)
57
-
58
- warn "\n#{c(app.name, :name)} is currently stopped, start it with 'cf start'" unless app.started?
59
- end
60
-
61
52
  def setup_new_app(path)
62
53
  self.path = path
63
54
  app = create_app(get_inputs)
@@ -70,6 +61,16 @@ module CF::App
70
61
 
71
62
  private
72
63
 
64
+ def sync_app(app, path)
65
+ upload_app(app, path)
66
+ apply_changes(app)
67
+ input[:path]
68
+ display_changes(app)
69
+ commit_changes(app)
70
+
71
+ warn "\n#{c(app.name, :name)} is currently stopped, start it with 'cf start'" unless app.started?
72
+ end
73
+
73
74
  def url_choices(name)
74
75
  client.current_space.domains.sort_by(&:name).collect do |d|
75
76
  # TODO: check availability
@@ -100,6 +100,7 @@ module CF::App
100
100
  while true
101
101
  if any_instance_flapping?(instances) || seconds == APP_CHECK_LIMIT
102
102
  err "Push unsuccessful."
103
+ line "#{c("TIP: The system will continue to attempt restarting all requested app instances that have crashed. Try 'cf app' to monitor app status. To troubleshoot crashes, try 'cf events' and 'cf crashlogs'.", :warning)}"
103
104
  return
104
105
  end
105
106
 
@@ -108,8 +109,9 @@ module CF::App
108
109
 
109
110
  indented { print_instances_summary(instances) }
110
111
 
111
- if all_instances_running?(instances)
112
+ if one_instance_running?(instances)
112
113
  line "#{c("Push successful! App '#{app.name}' available at http://#{app.host}.#{app.domain}", :good)}"
114
+ line "#{c("TIP: The system will continue to start all requested app instances. Try 'cf app' to monitor app status.", :warning)}"
113
115
  return
114
116
  end
115
117
  rescue CFoundry::NotStaged
@@ -124,8 +126,8 @@ module CF::App
124
126
  end
125
127
  end
126
128
 
127
- def all_instances_running?(instances)
128
- instances.all? { |i| i.state == "RUNNING" }
129
+ def one_instance_running?(instances)
130
+ instances.any? { |i| i.state == "RUNNING" }
129
131
  end
130
132
 
131
133
  def any_instance_flapping?(instances)
@@ -145,9 +147,9 @@ module CF::App
145
147
  end
146
148
 
147
149
  states = []
148
- %w{RUNNING STARTING DOWN FLAPPING}.each do |state|
150
+ %w{RUNNING STARTING FLAPPING DOWN}.each do |state|
149
151
  if (num = counts[state]) > 0
150
- states << "#{b(num)} #{c(state.downcase, state_color(state))}"
152
+ states << "#{b(num)} #{c(for_output(state), state_color(state))}"
151
153
  end
152
154
  end
153
155
 
@@ -157,5 +159,11 @@ module CF::App
157
159
  ratio = "#{running}#{d(" of ")}#{total} instances running"
158
160
  line "#{ratio} (#{states.join(", ")})"
159
161
  end
162
+
163
+ private
164
+ def for_output(state)
165
+ state = "CRASHING" if state == "FLAPPING"
166
+ state.downcase
167
+ end
160
168
  end
161
169
  end
@@ -8,7 +8,6 @@ module CF::App
8
8
  :argument => true, :from_given => by_name(:app)
9
9
  def stats
10
10
  app = input[:app]
11
-
12
11
  stats =
13
12
  with_progress("Getting stats for #{c(app.name, :name)}") do |s|
14
13
  begin
@@ -1,11 +1,28 @@
1
- require 'active_support/core_ext/object/try'
2
-
3
1
  class TargetPrettifier
4
2
  def self.prettify(client, outputter)
3
+
4
+ target = nil
5
+ version = nil
6
+ current_user_email = nil
7
+ current_space_name = nil
8
+ current_org_name = nil
9
+
10
+ if client
11
+ target = client.target
12
+ version = client.version
13
+ current_user = client.current_user
14
+ current_space = client.current_space
15
+ current_org = client.current_organization
16
+
17
+ current_user_email = current_user.email if current_user
18
+ current_space_name = current_space.name if current_space
19
+ current_org_name = current_org.name if current_org
20
+ end
21
+
5
22
  outputter.line("Target Information (where will apps be pushed):")
6
- outputter.line(" CF instance: #{print_var(client.try(:target), outputter)} (API version: #{print_var(client.try(:version), outputter)})")
7
- outputter.line(" user: #{print_var(client.try(:current_user).try(:email), outputter)}")
8
- outputter.line(" target app space: #{print_var(client.try(:current_space).try(:name), outputter)} (org: #{print_var(client.try(:current_organization).try(:name), outputter)})")
23
+ outputter.line(" CF instance: #{print_var(target, outputter)} (API version: #{print_var(version, outputter)})")
24
+ outputter.line(" user: #{print_var(current_user_email, outputter)}")
25
+ outputter.line(" target app space: #{print_var(current_space_name, outputter)} (org: #{print_var(current_org_name, outputter)})")
9
26
  end
10
27
 
11
28
  def self.print_var(object_name, outputter)
data/lib/cf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module CF
2
- VERSION = "5.2.1.rc4".freeze
2
+ VERSION = "5.2.1.rc5".freeze
3
3
  end
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ describe CFAdmin::ServiceBroker::Update do
4
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_admin_dir" }
5
+ stub_home_dir_with { fake_home_dir }
6
+
7
+ let(:client) { build(:client) }
8
+
9
+ let(:service_broker) { CFoundry::V2::ServiceBroker.new(nil, client) }
10
+
11
+ before do
12
+ CFAdmin::ServiceBroker::Update.client = client
13
+ client.stub(:service_broker_by_name).with('cf-mysql').and_return(service_broker)
14
+ end
15
+
16
+ it "updates a service broker when arguments are provided on the command line" do
17
+ service_broker.stub(:update!)
18
+
19
+ cf %W[update-service-broker --broker cf-mysql --name cf-othersql --url http://other.cfapp.io --token secret2]
20
+
21
+ service_broker.name.should == 'cf-othersql'
22
+ service_broker.broker_url.should == 'http://other.cfapp.io'
23
+ service_broker.token.should == 'secret2'
24
+
25
+ service_broker.should have_received(:update!)
26
+ end
27
+
28
+ it "updates a service broker when no change arguments are provided" do
29
+ service_broker.stub(:update!)
30
+
31
+ stub_ask("Name").and_return("cf-othersql")
32
+ stub_ask("URL").and_return("http://other.example.com")
33
+ stub_ask("Token").and_return("token2")
34
+
35
+ cf %W[update-service-broker cf-mysql]
36
+
37
+ service_broker.name.should == 'cf-othersql'
38
+ service_broker.broker_url.should == 'http://other.example.com'
39
+ service_broker.token.should == 'token2'
40
+
41
+ service_broker.should have_received(:update!)
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ ---
2
+ applications:
3
+ - name: hello-sinatra-1377212364_2782688
4
+ memory: 256M
5
+ instances: 1
6
+ host: hello-sinatra-subdomain-1377212364_2782688
7
+ domain: cfapps.io
8
+ path: .
9
+ services:
10
+ user-provided-1377212364_2782688:
11
+ label: user-provided
12
+ credentials:
13
+ username: abc123
14
+ password: sunshine
15
+ hostname: oracle.enterprise.com
16
+ port: 1234
17
+ name: myoracledb2
@@ -0,0 +1,261 @@
1
+ require "spec_helper"
2
+ require "cf/cli/app/env"
3
+
4
+ module CF
5
+ module App
6
+ describe Env do
7
+ before do
8
+ stub_client
9
+ end
10
+
11
+ describe '#set_env' do
12
+ let(:mock_restart_command) { MockRestartCommand.new }
13
+ let(:mock_start_command) { MockStartCommand.new }
14
+ let(:client) { build(:client) }
15
+ let(:app) { build(:app, client: client, name: 'puppies-r-us-website') }
16
+ let(:env) { Env.new(Mothership.commands[:set_env]) }
17
+
18
+ before do
19
+ client.stub(:app_by_name).and_return(app)
20
+ env.input = Mothership::Inputs.new(Mothership.commands[:set_env], env, inputs, {}, {})
21
+ end
22
+
23
+ context 'when name and value are specified' do
24
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', value: 'oh_what_a_value'} }
25
+
26
+ it 'updates the app with the new environment variable' do
27
+ expect(app.env['MY_LOVELY_SETTING']).to eq(nil)
28
+ expect(app).to receive(:update!) do
29
+ expect(app.env['MY_LOVELY_SETTING']).to eq('oh_what_a_value')
30
+ end
31
+
32
+ capture_output { env.set_env }
33
+ end
34
+ end
35
+
36
+ context "when the environment variable is specified with the 'VAR=VALUE' format" do
37
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING=oh_what_a_value'} }
38
+
39
+ it 'updates the app with the new environment variable' do
40
+ expect(app.env['MY_LOVELY_SETTING']).to eq(nil)
41
+ expect(app).to receive(:update!) do
42
+ expect(app.env['MY_LOVELY_SETTING']).to eq('oh_what_a_value')
43
+ end
44
+
45
+ capture_output { env.set_env }
46
+ end
47
+ end
48
+
49
+ describe "environment variable format" do
50
+ context "when the name has only letters, numbers, and underscores" do
51
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING123', value: "oh_what_a_value"} }
52
+ it "gets the value from the input[:value]" do
53
+ expect(app.env['MY_LOVELY_SETTING123']).to eq(nil)
54
+ app.stub(:update!)
55
+
56
+ capture_output { env.set_env }
57
+ expect(app.env['MY_LOVELY_SETTING123']).to eq("oh_what_a_value")
58
+ end
59
+ end
60
+
61
+ context "when the name has one equal sign, and is otherwise valid" do
62
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING123=oh_what_a_value'} }
63
+ it "gets the value by parsing the name" do
64
+ expect(app.env['MY_LOVELY_SETTING123']).to eq(nil)
65
+ app.stub(:update!)
66
+
67
+ capture_output { env.set_env }
68
+ expect(app.env['MY_LOVELY_SETTING123']).to eq("oh_what_a_value")
69
+ end
70
+
71
+ context "but provides a value anyway" do
72
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING123=oh_what_a_value', value: 'my_other_value'} }
73
+
74
+ it "gives the user an error" do
75
+ capture_exceptional_output { env.set_env }
76
+ expect(error_output).to say "You attempted to specify the value of MY_LOVELY_SETTING123 twice."
77
+ end
78
+ end
79
+ end
80
+
81
+ context "when the name has more than one equal sign" do
82
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING123=oh_what_a_value=badbadbad'} }
83
+ it "gives the user an error" do
84
+ capture_exceptional_output { env.set_env }
85
+ expect(error_output).to say "Invalid format: environment variable definition contains too many occurences of '='"
86
+ end
87
+ end
88
+
89
+ context "when there are characters that are not letters, numbers, or underscores" do
90
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING1?#$=value'} }
91
+ it "raises an error" do
92
+ capture_exceptional_output { env.set_env }
93
+ expect(error_output).to say "Invalid format: environment variable names can only contain alphanumeric characters and underscores"
94
+ end
95
+ end
96
+
97
+ context "when the name starts with a number" do
98
+ let(:inputs) { {app: app, name: '1MY_LOVELY_SETTING', value: 'oh_what_a_value'} }
99
+ it "raises an error" do
100
+ capture_exceptional_output { env.set_env }
101
+ expect(error_output).to say "Invalid format: environment variable names cannot start with a number"
102
+ end
103
+ end
104
+ end
105
+
106
+ context 'when the app is not started' do
107
+ context 'with the --restart flag' do
108
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', value: 'oh_what_a_value', restart: true} }
109
+ before { Start.stub(:new).and_return(mock_start_command) }
110
+
111
+ it 'starts the app' do
112
+ expect(app).to receive(:update!) do
113
+ expect(mock_start_command.started_apps).to be_empty
114
+ end
115
+
116
+ capture_output { env.set_env }
117
+ expect(output).to say "Your app was unstarted. Starting now."
118
+
119
+ expect(mock_start_command.started_apps).to eq [app]
120
+ end
121
+ end
122
+
123
+ context 'without the --restart flag' do
124
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', value: 'oh_what_a_value', restart: false} }
125
+
126
+ before do
127
+ app.stub(:update!)
128
+ end
129
+
130
+ it "suggests a 'cf push' to the user" do
131
+ capture_output { env.set_env }
132
+ expect(output).to say "TIP: Use 'cf push' to ensure your env variable changes take effect."
133
+ end
134
+ end
135
+ end
136
+
137
+ context 'when the app is already started' do
138
+ before do
139
+ app.stub(:started?) { true }
140
+ Restart.stub(:new).and_return(mock_restart_command)
141
+ end
142
+
143
+ context 'with the --restart flag' do
144
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', value: 'oh_what_a_value', restart: true} }
145
+
146
+ it 'restarts the app' do
147
+ expect(app).to receive(:update!) do
148
+ expect(mock_restart_command.restarted_apps).to be_empty
149
+ end
150
+
151
+ capture_output { env.set_env }
152
+
153
+ expect(mock_restart_command.restarted_apps).to eq([app])
154
+ end
155
+ end
156
+
157
+ context 'without the --restart flag' do
158
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', value: 'oh_what_a_value', restart: false} }
159
+ before do
160
+ app.stub(:update!)
161
+ end
162
+
163
+ it 'does not restart the app but suggests a cf push' do
164
+ capture_output { env.set_env }
165
+ expect(mock_restart_command.restarted_apps).to be_empty
166
+ expect(output).to say "TIP: Use 'cf push' to ensure your env variable changes take effect."
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '#unset_env' do
173
+ let(:app) { build(:app, client: client, name: 'puppies-r-us-website', environment_json: {'MY_LOVELY_SETTING' => 'oh_so_lovely'}) }
174
+ let(:client) { build(:client) }
175
+ let(:env) { Env.new(Mothership.commands[:unset_env]) }
176
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING'} }
177
+ let(:mock_start_command) { MockStartCommand.new }
178
+ let(:mock_restart_command) { MockRestartCommand.new }
179
+
180
+ before do
181
+ env.input = Mothership::Inputs.new(Mothership.commands[:unset_env], env, inputs, {}, {})
182
+ end
183
+
184
+ it 'unsets the environment variable and updates the app' do
185
+ expect(app.env['MY_LOVELY_SETTING']).to eq('oh_so_lovely')
186
+ expect(app).to receive(:update!) do
187
+ expect(app.env['MY_LOVELY_SETTING']).to be_nil
188
+ end
189
+
190
+ capture_output { env.unset_env }
191
+ end
192
+
193
+ context 'when the app is not started' do
194
+ context 'with the --restart flag' do
195
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', restart: true} }
196
+ before { Start.stub(:new).and_return(mock_start_command) }
197
+
198
+ it 'starts the app' do
199
+ expect(app).to receive(:update!) do
200
+ expect(mock_start_command.started_apps).to be_empty
201
+ end
202
+
203
+ capture_output { env.unset_env }
204
+ expect(output).to say "Your app was unstarted. Starting now."
205
+
206
+ expect(mock_start_command.started_apps).to eq [app]
207
+ end
208
+ end
209
+
210
+ context 'without the --restart flag' do
211
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING'} }
212
+
213
+ before do
214
+ app.stub(:update!)
215
+ end
216
+
217
+ it "suggests a 'cf push' to the user" do
218
+ capture_output { env.unset_env }
219
+ expect(output).to say "TIP: Use 'cf push' to ensure your env variable changes take effect."
220
+ end
221
+ end
222
+ end
223
+
224
+ context 'when the app is already started' do
225
+ before do
226
+ app.stub(:started?) { true }
227
+ Restart.stub(:new).and_return(mock_restart_command)
228
+ end
229
+
230
+ context 'with the --restart flag' do
231
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', restart: true} }
232
+
233
+ it 'restarts the app' do
234
+ expect(app).to receive(:update!) do
235
+ expect(mock_restart_command.restarted_apps).to be_empty
236
+ end
237
+
238
+ capture_output { env.unset_env }
239
+
240
+ expect(mock_restart_command.restarted_apps).to eq([app])
241
+ end
242
+ end
243
+
244
+ context 'without the --restart flag' do
245
+ let(:inputs) { {app: app, name: 'MY_LOVELY_SETTING', restart: false} }
246
+ before do
247
+ app.stub(:update!)
248
+ end
249
+
250
+ it 'does not restart the app but suggests a cf push' do
251
+ capture_output { env.unset_env }
252
+ expect(mock_restart_command.restarted_apps).to be_empty
253
+ expect(output).to say "TIP: Use 'cf push' to ensure your env variable changes take effect."
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
261
+