cf 5.2.1.rc4 → 5.2.1.rc5

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/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
+