trucker-cli 0.0.1

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 (294) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +1277 -0
  3. data/Rakefile +13 -0
  4. data/bin/truck +18 -0
  5. data/lib/admin/README.md +30 -0
  6. data/lib/admin/curl.rb +59 -0
  7. data/lib/admin/guid.rb +89 -0
  8. data/lib/admin/plugin.rb +8 -0
  9. data/lib/admin/service_auth_token.rb +93 -0
  10. data/lib/admin/service_broker/add.rb +54 -0
  11. data/lib/admin/service_broker/remove.rb +33 -0
  12. data/lib/admin/service_broker/service_brokers.rb +29 -0
  13. data/lib/admin/service_broker/update.rb +60 -0
  14. data/lib/admin/set_quota.rb +44 -0
  15. data/lib/cf.rb +14 -0
  16. data/lib/cf/cli.rb +495 -0
  17. data/lib/cf/cli/app/app.rb +43 -0
  18. data/lib/cf/cli/app/apps.rb +87 -0
  19. data/lib/cf/cli/app/base.rb +72 -0
  20. data/lib/cf/cli/app/delete.rb +95 -0
  21. data/lib/cf/cli/app/deprecated.rb +11 -0
  22. data/lib/cf/cli/app/env.rb +99 -0
  23. data/lib/cf/cli/app/events.rb +45 -0
  24. data/lib/cf/cli/app/files.rb +137 -0
  25. data/lib/cf/cli/app/health.rb +26 -0
  26. data/lib/cf/cli/app/instances.rb +53 -0
  27. data/lib/cf/cli/app/logs.rb +76 -0
  28. data/lib/cf/cli/app/push.rb +104 -0
  29. data/lib/cf/cli/app/push/create.rb +108 -0
  30. data/lib/cf/cli/app/push/interactions.rb +90 -0
  31. data/lib/cf/cli/app/push/sync.rb +57 -0
  32. data/lib/cf/cli/app/rename.rb +35 -0
  33. data/lib/cf/cli/app/restart.rb +31 -0
  34. data/lib/cf/cli/app/scale.rb +63 -0
  35. data/lib/cf/cli/app/start.rb +175 -0
  36. data/lib/cf/cli/app/stats.rb +66 -0
  37. data/lib/cf/cli/app/stop.rb +27 -0
  38. data/lib/cf/cli/domain/base.rb +9 -0
  39. data/lib/cf/cli/domain/domains.rb +40 -0
  40. data/lib/cf/cli/domain/map.rb +55 -0
  41. data/lib/cf/cli/domain/unmap.rb +56 -0
  42. data/lib/cf/cli/help.rb +15 -0
  43. data/lib/cf/cli/interactive.rb +104 -0
  44. data/lib/cf/cli/login_requirements.rb +13 -0
  45. data/lib/cf/cli/organization/base.rb +12 -0
  46. data/lib/cf/cli/organization/create.rb +36 -0
  47. data/lib/cf/cli/organization/delete.rb +61 -0
  48. data/lib/cf/cli/organization/org.rb +45 -0
  49. data/lib/cf/cli/organization/orgs.rb +30 -0
  50. data/lib/cf/cli/organization/rename.rb +36 -0
  51. data/lib/cf/cli/populators/base.rb +16 -0
  52. data/lib/cf/cli/populators/organization.rb +32 -0
  53. data/lib/cf/cli/populators/populator_methods.rb +64 -0
  54. data/lib/cf/cli/populators/space.rb +33 -0
  55. data/lib/cf/cli/populators/target.rb +14 -0
  56. data/lib/cf/cli/route/base.rb +9 -0
  57. data/lib/cf/cli/route/delete.rb +28 -0
  58. data/lib/cf/cli/route/map.rb +68 -0
  59. data/lib/cf/cli/route/routes.rb +26 -0
  60. data/lib/cf/cli/route/unmap.rb +56 -0
  61. data/lib/cf/cli/service/base.rb +9 -0
  62. data/lib/cf/cli/service/bind.rb +43 -0
  63. data/lib/cf/cli/service/create.rb +158 -0
  64. data/lib/cf/cli/service/delete.rb +82 -0
  65. data/lib/cf/cli/service/rename.rb +35 -0
  66. data/lib/cf/cli/service/service.rb +40 -0
  67. data/lib/cf/cli/service/service_helper.rb +25 -0
  68. data/lib/cf/cli/service/service_instance_helper.rb +100 -0
  69. data/lib/cf/cli/service/services.rb +110 -0
  70. data/lib/cf/cli/service/unbind.rb +36 -0
  71. data/lib/cf/cli/space/base.rb +27 -0
  72. data/lib/cf/cli/space/create.rb +66 -0
  73. data/lib/cf/cli/space/delete.rb +55 -0
  74. data/lib/cf/cli/space/rename.rb +38 -0
  75. data/lib/cf/cli/space/space.rb +66 -0
  76. data/lib/cf/cli/space/spaces.rb +57 -0
  77. data/lib/cf/cli/space/switch.rb +18 -0
  78. data/lib/cf/cli/start/base.rb +35 -0
  79. data/lib/cf/cli/start/colors.rb +13 -0
  80. data/lib/cf/cli/start/login.rb +104 -0
  81. data/lib/cf/cli/start/logout.rb +17 -0
  82. data/lib/cf/cli/start/target.rb +50 -0
  83. data/lib/cf/cli/start/target_prettifier.rb +35 -0
  84. data/lib/cf/cli/start/targets.rb +16 -0
  85. data/lib/cf/cli/user/base.rb +29 -0
  86. data/lib/cf/cli/user/create.rb +51 -0
  87. data/lib/cf/cli/user/passwd.rb +36 -0
  88. data/lib/cf/cli/user/register.rb +42 -0
  89. data/lib/cf/cli/user/users.rb +32 -0
  90. data/lib/cf/constants.rb +10 -0
  91. data/lib/cf/errors.rb +19 -0
  92. data/lib/cf/plugin.rb +56 -0
  93. data/lib/cf/spacing.rb +89 -0
  94. data/lib/cf/spec_helper.rb +1 -0
  95. data/lib/cf/test_support.rb +6 -0
  96. data/lib/cf/version.rb +3 -0
  97. data/lib/console/README.md +16 -0
  98. data/lib/console/console.rb +187 -0
  99. data/lib/console/plugin.rb +32 -0
  100. data/lib/manifests/errors.rb +35 -0
  101. data/lib/manifests/loader.rb +31 -0
  102. data/lib/manifests/loader/builder.rb +39 -0
  103. data/lib/manifests/loader/normalizer.rb +145 -0
  104. data/lib/manifests/loader/resolver.rb +79 -0
  105. data/lib/manifests/manifests.rb +343 -0
  106. data/lib/manifests/plugin.rb +140 -0
  107. data/lib/micro/README.md +25 -0
  108. data/lib/micro/errors.rb +4 -0
  109. data/lib/micro/micro.rb +56 -0
  110. data/lib/micro/plugin.rb +197 -0
  111. data/lib/micro/switcher/base.rb +79 -0
  112. data/lib/micro/switcher/darwin.rb +21 -0
  113. data/lib/micro/switcher/dummy.rb +15 -0
  114. data/lib/micro/switcher/linux.rb +16 -0
  115. data/lib/micro/switcher/windows.rb +31 -0
  116. data/lib/micro/vmrun.rb +175 -0
  117. data/lib/tasks/gem_release.rake +56 -0
  118. data/lib/tasks/windows_build.rake +14 -0
  119. data/lib/tunnel/README.md +29 -0
  120. data/lib/tunnel/config/clients.yml +17 -0
  121. data/lib/tunnel/helper-app/Gemfile +10 -0
  122. data/lib/tunnel/helper-app/Gemfile.lock +48 -0
  123. data/lib/tunnel/helper-app/server.rb +43 -0
  124. data/lib/tunnel/plugin.rb +183 -0
  125. data/lib/tunnel/tunnel.rb +295 -0
  126. data/spec/admin/curl_spec.rb +52 -0
  127. data/spec/admin/guid_spec.rb +85 -0
  128. data/spec/admin/service_broker/add_spec.rb +64 -0
  129. data/spec/admin/service_broker/remove_spec.rb +46 -0
  130. data/spec/admin/service_broker/service_brokers_spec.rb +34 -0
  131. data/spec/admin/service_broker/update_spec.rb +51 -0
  132. data/spec/admin/set_quota_spec.rb +89 -0
  133. data/spec/assets/env/Gemfile +4 -0
  134. data/spec/assets/env/Gemfile.lock +20 -0
  135. data/spec/assets/env/env_test.rb +58 -0
  136. data/spec/assets/hello-sinatra/Gemfile +3 -0
  137. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  138. data/spec/assets/hello-sinatra/config.ru +3 -0
  139. data/spec/assets/hello-sinatra/fat-cat-makes-app-larger.png +0 -0
  140. data/spec/assets/hello-sinatra/main.rb +8 -0
  141. data/spec/assets/rails328_ruby187_app/Gemfile +39 -0
  142. data/spec/assets/rails328_ruby187_app/README.rdoc +261 -0
  143. data/spec/assets/rails328_ruby187_app/Rakefile +7 -0
  144. data/spec/assets/rails328_ruby187_app/app/assets/images/rails.png +0 -0
  145. data/spec/assets/rails328_ruby187_app/app/assets/javascripts/application.js +15 -0
  146. data/spec/assets/rails328_ruby187_app/app/assets/stylesheets/application.css +13 -0
  147. data/spec/assets/rails328_ruby187_app/app/controllers/application_controller.rb +3 -0
  148. data/spec/assets/rails328_ruby187_app/app/helpers/application_helper.rb +2 -0
  149. data/spec/assets/rails328_ruby187_app/app/views/layouts/application.html.erb +14 -0
  150. data/spec/assets/rails328_ruby187_app/config.ru +4 -0
  151. data/spec/assets/rails328_ruby187_app/config/application.rb +62 -0
  152. data/spec/assets/rails328_ruby187_app/config/boot.rb +6 -0
  153. data/spec/assets/rails328_ruby187_app/config/database.yml +25 -0
  154. data/spec/assets/rails328_ruby187_app/config/environment.rb +5 -0
  155. data/spec/assets/rails328_ruby187_app/config/environments/development.rb +37 -0
  156. data/spec/assets/rails328_ruby187_app/config/environments/production.rb +67 -0
  157. data/spec/assets/rails328_ruby187_app/config/environments/test.rb +37 -0
  158. data/spec/assets/rails328_ruby187_app/config/initializers/backtrace_silencers.rb +7 -0
  159. data/spec/assets/rails328_ruby187_app/config/initializers/inflections.rb +15 -0
  160. data/spec/assets/rails328_ruby187_app/config/initializers/mime_types.rb +5 -0
  161. data/spec/assets/rails328_ruby187_app/config/initializers/secret_token.rb +7 -0
  162. data/spec/assets/rails328_ruby187_app/config/initializers/session_store.rb +8 -0
  163. data/spec/assets/rails328_ruby187_app/config/initializers/wrap_parameters.rb +14 -0
  164. data/spec/assets/rails328_ruby187_app/config/locales/en.yml +5 -0
  165. data/spec/assets/rails328_ruby187_app/config/routes.rb +58 -0
  166. data/spec/assets/rails328_ruby187_app/db/seeds.rb +7 -0
  167. data/spec/assets/rails328_ruby187_app/doc/README_FOR_APP +2 -0
  168. data/spec/assets/rails328_ruby187_app/manifest.yml +7 -0
  169. data/spec/assets/rails328_ruby187_app/public/404.html +26 -0
  170. data/spec/assets/rails328_ruby187_app/public/422.html +26 -0
  171. data/spec/assets/rails328_ruby187_app/public/500.html +25 -0
  172. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css +0 -0
  173. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css.gz +0 -0
  174. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js +17 -0
  175. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js.gz +0 -0
  176. data/spec/assets/rails328_ruby187_app/public/assets/application.css +0 -0
  177. data/spec/assets/rails328_ruby187_app/public/assets/application.css.gz +0 -0
  178. data/spec/assets/rails328_ruby187_app/public/assets/application.js +17 -0
  179. data/spec/assets/rails328_ruby187_app/public/assets/application.js.gz +0 -0
  180. data/spec/assets/rails328_ruby187_app/public/assets/manifest.yml +4 -0
  181. data/spec/assets/rails328_ruby187_app/public/assets/rails-be8732dac73d845ac5b142c8fb5f9fb0.png +0 -0
  182. data/spec/assets/rails328_ruby187_app/public/assets/rails.png +0 -0
  183. data/spec/assets/rails328_ruby187_app/public/favicon.ico +0 -0
  184. data/spec/assets/rails328_ruby187_app/public/index.html +241 -0
  185. data/spec/assets/rails328_ruby187_app/public/robots.txt +5 -0
  186. data/spec/assets/rails328_ruby187_app/script/rails +6 -0
  187. data/spec/assets/rails328_ruby187_app/test/performance/browsing_test.rb +12 -0
  188. data/spec/assets/rails328_ruby187_app/test/test_helper.rb +13 -0
  189. data/spec/cf/cli/app/base_spec.rb +17 -0
  190. data/spec/cf/cli/app/delete_spec.rb +197 -0
  191. data/spec/cf/cli/app/env_spec.rb +261 -0
  192. data/spec/cf/cli/app/events_spec.rb +72 -0
  193. data/spec/cf/cli/app/help_spec.rb +39 -0
  194. data/spec/cf/cli/app/instances_spec.rb +69 -0
  195. data/spec/cf/cli/app/push/create_spec.rb +444 -0
  196. data/spec/cf/cli/app/push/interactions_spec.rb +32 -0
  197. data/spec/cf/cli/app/push_spec.rb +332 -0
  198. data/spec/cf/cli/app/rename_spec.rb +108 -0
  199. data/spec/cf/cli/app/restart_spec.rb +47 -0
  200. data/spec/cf/cli/app/scale_spec.rb +63 -0
  201. data/spec/cf/cli/app/start_spec.rb +255 -0
  202. data/spec/cf/cli/app/stats_spec.rb +66 -0
  203. data/spec/cf/cli/domain/help_spec.rb +39 -0
  204. data/spec/cf/cli/domain/map_spec.rb +140 -0
  205. data/spec/cf/cli/domain/unmap_spec.rb +77 -0
  206. data/spec/cf/cli/help_spec.rb +21 -0
  207. data/spec/cf/cli/organization/create_spec.rb +121 -0
  208. data/spec/cf/cli/organization/delete_spec.rb +105 -0
  209. data/spec/cf/cli/organization/help_spec.rb +39 -0
  210. data/spec/cf/cli/organization/orgs_spec.rb +116 -0
  211. data/spec/cf/cli/organization/rename_spec.rb +116 -0
  212. data/spec/cf/cli/populators/organization_spec.rb +179 -0
  213. data/spec/cf/cli/populators/space_spec.rb +156 -0
  214. data/spec/cf/cli/populators/target_spec.rb +39 -0
  215. data/spec/cf/cli/route/delete_spec.rb +92 -0
  216. data/spec/cf/cli/route/help_spec.rb +32 -0
  217. data/spec/cf/cli/route/map_spec.rb +148 -0
  218. data/spec/cf/cli/route/unmap_spec.rb +114 -0
  219. data/spec/cf/cli/service/bind_spec.rb +25 -0
  220. data/spec/cf/cli/service/create_spec.rb +147 -0
  221. data/spec/cf/cli/service/delete_spec.rb +22 -0
  222. data/spec/cf/cli/service/help_spec.rb +39 -0
  223. data/spec/cf/cli/service/rename_spec.rb +103 -0
  224. data/spec/cf/cli/service/service_helper_spec.rb +66 -0
  225. data/spec/cf/cli/service/service_instance_helper_spec.rb +155 -0
  226. data/spec/cf/cli/service/service_spec.rb +23 -0
  227. data/spec/cf/cli/service/services_spec.rb +156 -0
  228. data/spec/cf/cli/service/unbind_spec.rb +25 -0
  229. data/spec/cf/cli/space/base_spec.rb +49 -0
  230. data/spec/cf/cli/space/create_spec.rb +144 -0
  231. data/spec/cf/cli/space/delete_spec.rb +109 -0
  232. data/spec/cf/cli/space/help_spec.rb +39 -0
  233. data/spec/cf/cli/space/rename_spec.rb +110 -0
  234. data/spec/cf/cli/space/space_spec.rb +80 -0
  235. data/spec/cf/cli/space/spaces_spec.rb +107 -0
  236. data/spec/cf/cli/space/switch_space_spec.rb +57 -0
  237. data/spec/cf/cli/start/help_spec.rb +39 -0
  238. data/spec/cf/cli/start/login_spec.rb +142 -0
  239. data/spec/cf/cli/start/logout_spec.rb +49 -0
  240. data/spec/cf/cli/start/target_prettifier_spec.rb +78 -0
  241. data/spec/cf/cli/start/target_spec.rb +123 -0
  242. data/spec/cf/cli/user/create_spec.rb +98 -0
  243. data/spec/cf/cli/user/help_spec.rb +32 -0
  244. data/spec/cf/cli/user/passwd_spec.rb +94 -0
  245. data/spec/cf/cli/user/register_spec.rb +145 -0
  246. data/spec/cf/cli_spec.rb +474 -0
  247. data/spec/console/console_spec.rb +189 -0
  248. data/spec/factories/cfoundry/v2/apps_factory.rb +12 -0
  249. data/spec/factories/cfoundry/v2/clients_factory.rb +7 -0
  250. data/spec/factories/cfoundry/v2/domains_factory.rb +8 -0
  251. data/spec/factories/cfoundry/v2/managed_service_instances_factory.rb +13 -0
  252. data/spec/factories/cfoundry/v2/organizations_factory.rb +14 -0
  253. data/spec/factories/cfoundry/v2/quota_definitions.rb +13 -0
  254. data/spec/factories/cfoundry/v2/routes_factory.rb +8 -0
  255. data/spec/factories/cfoundry/v2/service_bindings_factory.rb +11 -0
  256. data/spec/factories/cfoundry/v2/service_brokers_factory.rb +11 -0
  257. data/spec/factories/cfoundry/v2/service_plans_factory.rb +17 -0
  258. data/spec/factories/cfoundry/v2/services_factory.rb +14 -0
  259. data/spec/factories/cfoundry/v2/spaces_factory.rb +13 -0
  260. data/spec/factories/cfoundry/v2/stacks_factory.rb +11 -0
  261. data/spec/factories/cfoundry/v2/user_provided_service_instances_factory.rb +12 -0
  262. data/spec/factories/cfoundry/v2/users_factory.rb +7 -0
  263. data/spec/features/backspace_spec.rb +46 -0
  264. data/spec/features/create_user_spec.rb +74 -0
  265. data/spec/features/delete_orphaned_service_spec.rb +64 -0
  266. data/spec/features/login_spec.rb +65 -0
  267. data/spec/features/manifests_spec.rb +86 -0
  268. data/spec/features/org_spec.rb +50 -0
  269. data/spec/features/push_flow_spec.rb +169 -0
  270. data/spec/features/service_brokers_spec.rb +173 -0
  271. data/spec/features/services_spec.rb +122 -0
  272. data/spec/features/space_spec.rb +107 -0
  273. data/spec/features/switching_targets_spec.rb +70 -0
  274. data/spec/manifests/errors_spec.rb +43 -0
  275. data/spec/manifests/loader/builder_spec.rb +80 -0
  276. data/spec/manifests/loader/normalizer_spec.rb +158 -0
  277. data/spec/manifests/manifests_spec.rb +335 -0
  278. data/spec/manifests/plugin_spec.rb +410 -0
  279. data/spec/micro/plugin_spec.rb +64 -0
  280. data/spec/spec_helper.rb +69 -0
  281. data/spec/support/cli_helper.rb +87 -0
  282. data/spec/support/config_helper.rb +15 -0
  283. data/spec/support/factory_girl.rb +6 -0
  284. data/spec/support/fake_home_dir.rb +55 -0
  285. data/spec/support/features_helper.rb +99 -0
  286. data/spec/support/interact_helper.rb +27 -0
  287. data/spec/support/matchers.rb +16 -0
  288. data/spec/support/mock_commands/mock_restart.rb +16 -0
  289. data/spec/support/mock_commands/mock_start.rb +17 -0
  290. data/spec/support/shared_examples/errors.rb +40 -0
  291. data/spec/support/shared_examples/input.rb +14 -0
  292. data/spec/tunnel/plugin_spec.rb +31 -0
  293. data/spec/tunnel/tunnel_spec.rb +54 -0
  294. metadata +814 -0
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+ require "webmock/rspec"
3
+
4
+ if ENV["CF_V2_RUN_INTEGRATION"]
5
+ describe "A user deleting an app bound to a user-provided service" do
6
+ let(:run_id) { TRAVIS_BUILD_ID.to_s + Time.new.to_f.to_s.gsub(".", "_") }
7
+ let(:app) { "hello-sinatra-#{run_id}" }
8
+ let(:user_provided_name) { "user-provided-#{run_id}"}
9
+
10
+ before do
11
+ FileUtils.rm_rf File.expand_path(CF::CONFIG_DIR)
12
+ WebMock.allow_net_connect!
13
+ login
14
+ end
15
+
16
+ after do
17
+ `#{cf_bin} unbind-service -f --no-script #{user_provided_name} #{app}`
18
+ `#{cf_bin} delete-service -f --no-script #{user_provided_name}`
19
+
20
+ `#{cf_bin} delete #{app} -f --routes --no-script`
21
+ logout
22
+ end
23
+
24
+ it "can delete the user-provided service when given --delete-orphaned" do
25
+ Dir.chdir("#{SPEC_ROOT}/assets/hello-sinatra") do
26
+ FileUtils.rm("manifest.yml", force: true)
27
+ File.open("manifest.yml", "w") do |f|
28
+ f.write(<<-MANIFEST)
29
+ ---
30
+ applications:
31
+ - name: #{app}
32
+ memory: 256M
33
+ instances: 1
34
+ host: ~
35
+ domain: none
36
+ path: .
37
+ services:
38
+ #{user_provided_name}:
39
+ label: user-provided
40
+ credentials:
41
+ username: abc123
42
+ MANIFEST
43
+ end
44
+
45
+ BlueShell::Runner.run("#{cf_bin} push --no-start") do |runner|
46
+ expect(runner).to say "Using manifest file manifest.yml"
47
+ expect(runner).to say "Creating #{app}... OK"
48
+
49
+ expect(runner).to say /Creating service #{user_provided_name}.*OK/
50
+ expect(runner).to say /Binding #{user_provided_name} to #{app}... OK/
51
+
52
+ expect(runner).to say "Uploading #{app}... OK"
53
+ end
54
+ end
55
+
56
+ BlueShell::Runner.run("#{cf_bin} delete #{app} --delete-orphaned --force") do |runner|
57
+ expect(runner).to say "Deleting #{app}... OK"
58
+ expect(runner).to say "Deleting #{user_provided_name}... OK"
59
+ end
60
+ end
61
+ end
62
+ else
63
+ $stderr.puts 'Skipping v2 integration specs; please provide necessary environment variables'
64
+ end
@@ -0,0 +1,65 @@
1
+ require "spec_helper"
2
+
3
+ if ENV['CF_V2_RUN_INTEGRATION']
4
+ describe "A user logs in", :ruby19 => true do
5
+
6
+ let(:target) { ENV['CF_V2_TEST_TARGET'] }
7
+ let(:second_username) { ENV['CF_V2_TEST_USER'] }
8
+ let(:second_password) { ENV['CF_V2_TEST_PASSWORD'] }
9
+ let(:second_organization) { ENV['CF_V2_TEST_ORGANIZATION'] }
10
+ let(:second_space) {ENV['CF_V2_TEST_SPACE']}
11
+
12
+ let(:username) { ENV['CF_V2_OTHER_TEST_USER'] }
13
+ let(:organization) { ENV['CF_V2_OTHER_TEST_ORGANIZATION'] }
14
+ let(:space) { ENV['CF_V2_OTHER_TEST_SPACE'] }
15
+ let(:password) { ENV['CF_V2_OTHER_TEST_PASSWORD'] || ENV['CF_V2_TEST_PASSWORD'] }
16
+
17
+ before do
18
+ Interact::Progress::Dots.start!
19
+ set_target
20
+ logout
21
+ end
22
+
23
+ after do
24
+ logout
25
+ Interact::Progress::Dots.stop!
26
+ end
27
+
28
+ context "when a different user is already logged in" do
29
+ before do
30
+ BlueShell::Runner.run("#{cf_bin} login #{username} --password #{password}") do |runner|
31
+ expect(runner).to say "Authenticating... OK"
32
+
33
+ expect(runner).to say(
34
+ "Organization>" => proc {
35
+ runner.send_keys organization
36
+ expect(runner).to say /Switching to organization .*\.\.\. OK/
37
+ },
38
+ "Switching to organization" => proc {}
39
+ )
40
+
41
+ expect(runner).to say(
42
+ "Space>" => proc {
43
+ runner.send_keys "1"
44
+ expect(runner).to say /Switching to space .*\.\.\. OK/
45
+ },
46
+ "Switching to space" => proc {}
47
+ )
48
+
49
+ runner.wait_for_exit
50
+ end
51
+ end
52
+
53
+ it "can switch spaces on login" do
54
+ BlueShell::Runner.run("#{cf_bin} login #{second_username} --password #{second_password} --organization #{second_organization} --space #{second_space}") do |runner|
55
+ expect(runner).to say "Authenticating... OK"
56
+ expect(runner).to say "Switching to organization #{second_organization}... OK"
57
+ expect(runner).to say "Switching to space #{second_space}... OK"
58
+ runner.wait_for_exit
59
+ end
60
+ end
61
+ end
62
+ end
63
+ else
64
+ $stderr.puts 'Skipping v2 integration specs; please provide necessary environment variables'
65
+ end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+ require "webmock/rspec"
3
+
4
+ if ENV["CF_V2_RUN_INTEGRATION"]
5
+ describe "A user pushing a new sinatra app" do
6
+
7
+ let(:run_id) { TRAVIS_BUILD_ID.to_s + Time.new.to_f.to_s.gsub(".", "_") }
8
+ let(:app) { "hello-sinatra-#{run_id}" }
9
+ let(:subdomain) { "hello-sinatra-subdomain-#{run_id}" }
10
+ let(:user_provided_name) { "user-provided-#{run_id}"}
11
+
12
+ before do
13
+ FileUtils.rm_rf File.expand_path(CF::CONFIG_DIR)
14
+ WebMock.allow_net_connect!
15
+ login
16
+ end
17
+
18
+ after do
19
+ `#{cf_bin} unbind-service -f --no-script #{user_provided_name} #{app}`
20
+ `#{cf_bin} delete-service -f --no-script #{user_provided_name}`
21
+
22
+ `#{cf_bin} delete #{app} -f --routes --no-script`
23
+ logout
24
+ end
25
+
26
+ context "with user-provided service" do
27
+ it "reads the manifest when pushing" do
28
+ Dir.chdir("#{SPEC_ROOT}/assets/hello-sinatra") do
29
+ FileUtils.rm("manifest.yml", force: true)
30
+ File.open("manifest.yml", "w") do |f|
31
+ f.write(<<-MANIFEST)
32
+ ---
33
+ applications:
34
+ - name: #{app}
35
+ memory: 256M
36
+ instances: 1
37
+ host: #{subdomain}
38
+ domain: cfapps.io
39
+ path: .
40
+ services:
41
+ #{user_provided_name}:
42
+ label: user-provided
43
+ credentials:
44
+ username: abc123
45
+ password: sunshine
46
+ hostname: oracle.enterprise.com
47
+ port: 1234
48
+ name: myoracledb2
49
+ MANIFEST
50
+ end
51
+
52
+ BlueShell::Runner.run("#{cf_bin} push") do |runner|
53
+ expect(runner).to say "Using manifest file manifest.yml"
54
+ expect(runner).to say "Creating #{app}... OK"
55
+
56
+ expect(runner).to say(/Creating route #{subdomain}\..*\.\.\. OK/)
57
+ expect(runner).to say(/Binding #{subdomain}\..* to #{app}\.\.\. OK/)
58
+
59
+ expect(runner).to say /Creating service #{user_provided_name}.*OK/
60
+ expect(runner).to say /Binding #{user_provided_name} to #{app}... OK/
61
+
62
+ expect(runner).to say "Uploading #{app}... OK", 180
63
+ expect(runner).to say "Preparing to start #{app}... OK", 180
64
+ expect(runner).to say "Checking status of app '#{app}'...", 180
65
+ expect(runner).to say "1 of 1 instances running"
66
+ expect(runner).to say "Push successful! App '#{app}' available at #{subdomain}.cfapps.io", 30
67
+ end
68
+ end
69
+
70
+ BlueShell::Runner.run("#{cf_bin} services") do |runner|
71
+ expect(runner).to say /name\s+service\s+provider\s+version\s+plan\s+bound apps/
72
+ expect(runner).to say /#{user_provided_name}\s+ # name
73
+ user-provided\s+ # service
74
+ n\/a\s+ # provider
75
+ n\/a\s+ # version
76
+ n\/a\s+ # plan
77
+ #{app} # bound apps
78
+ /x
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ else
85
+ $stderr.puts 'Skipping v2 integration specs; please provide necessary environment variables'
86
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+
3
+ if ENV["CF_V2_RUN_INTEGRATION"]
4
+ describe "creating and deleting orgs", ruby19: true do
5
+ before(:all) { WebMock.allow_net_connect! }
6
+ after(:all) { WebMock.disable_net_connect! }
7
+
8
+ let(:run_id) { TRAVIS_BUILD_ID.to_s + Time.new.to_f.to_s.gsub(".", "_") }
9
+ let(:new_org_name) { "new-org-#{run_id}" }
10
+
11
+ before do
12
+ Interact::Progress::Dots.start!
13
+ login
14
+ end
15
+
16
+ after do
17
+ logout
18
+ Interact::Progress::Dots.stop!
19
+ end
20
+
21
+ it "can create and recursively delete an org" do
22
+ BlueShell::Runner.run("#{cf_bin} create-org #{new_org_name}") do |runner|
23
+ expect(runner).to say "Creating organization #{new_org_name}... OK"
24
+ expect(runner).to say "Switching to organization #{new_org_name}... OK"
25
+ expect(runner).to say "There are no spaces. You may want to create one with create-space."
26
+ end
27
+
28
+ BlueShell::Runner.run("#{cf_bin} create-space new-space") do |runner|
29
+ expect(runner).to say "Creating space new-space... OK"
30
+ end
31
+
32
+ # TODO: pending until cc change 442f08e72c0808baf85b948a8b56e58f025edf72 is on a1
33
+ #
34
+ # BlueShell::Runner.run("cf delete-org #{new_org_name} --force") do |runner|
35
+ # expect(runner).to say "If you want to delete the organization along with" +
36
+ # " all dependent objects, rerun the command with the '--recursive' flag."
37
+ # end
38
+
39
+ BlueShell::Runner.run("#{cf_bin} delete-org #{new_org_name} --force --recursive") do |runner|
40
+ expect(runner).to say "Deleting organization #{new_org_name}... OK", 15
41
+ end
42
+
43
+ BlueShell::Runner.run("#{cf_bin} target -o #{new_org_name}") do |runner|
44
+ expect(runner).to say "Unknown organization '#{new_org_name}'."
45
+ end
46
+ end
47
+ end
48
+ else
49
+ $stderr.puts 'Skipping v2 integration specs; please provide necessary environment variables'
50
+ end
@@ -0,0 +1,169 @@
1
+ require "spec_helper"
2
+ require "webmock/rspec"
3
+
4
+ if ENV["CF_V2_RUN_INTEGRATION"]
5
+ describe "A user pushing a new sinatra app", :ruby19 => true do
6
+ let(:run_id) { TRAVIS_BUILD_ID.to_s + Time.new.to_f.to_s.gsub(".", "_") }
7
+ let(:app) { "hello-sinatra-#{run_id}" }
8
+ let(:subdomain) { "hello-sinatra-subdomain-#{run_id}" }
9
+ let(:service_name) { "dummy-service-#{run_id}" }
10
+ let(:user_provided_name) { "user-provided-#{run_id}"}
11
+
12
+ before do
13
+ FileUtils.rm_rf File.expand_path(CF::CONFIG_DIR)
14
+ WebMock.allow_net_connect!
15
+ Interact::Progress::Dots.start!
16
+ login
17
+ end
18
+
19
+ after do
20
+ `#{cf_bin} unbind-service -f --no-script #{service_name} #{app}`
21
+ `#{cf_bin} delete-service -f --no-script #{service_name}`
22
+
23
+ `#{cf_bin} unbind-service -f --no-script #{user_provided_name} #{app}`
24
+ `#{cf_bin} delete-service -f --no-script #{user_provided_name}`
25
+
26
+ `#{cf_bin} delete #{app} -f --routes --no-script`
27
+ logout
28
+ Interact::Progress::Dots.stop!
29
+ end
30
+
31
+ it "excercises the app workflow" do
32
+ BlueShell::Runner.run("#{cf_bin} app #{app}") do |runner|
33
+ expect(runner).to say "Unknown app '#{app}'."
34
+ end
35
+
36
+ Dir.chdir("#{SPEC_ROOT}/assets/hello-sinatra") do
37
+ FileUtils.rm("manifest.yml", force: true)
38
+ BlueShell::Runner.run("#{cf_bin} push") do |runner|
39
+ expect(runner).to say "Name>"
40
+ runner.send_keys app
41
+
42
+ expect(runner).to say "Instances> 1"
43
+ runner.send_return
44
+
45
+ expect(runner).to_not say "Custom startup command> "
46
+
47
+ runner.send_up_arrow
48
+ expect(runner).to say "Instances> 1"
49
+ runner.send_return
50
+
51
+ expect(runner).to say "Memory Limit>"
52
+ runner.send_keys "128M"
53
+
54
+ expect(runner).to say "Creating #{app}... OK"
55
+
56
+ expect(runner).to say "Subdomain> #{app}"
57
+
58
+ runner.send_up_arrow
59
+ expect(runner).not_to say "Memory Limit>"
60
+ runner.send_keys subdomain
61
+
62
+ expect(runner).to say "1:"
63
+ expect(runner).to say "Domain>"
64
+ runner.send_keys "1"
65
+
66
+ expect(runner).to say(/Creating route #{subdomain}\..*\.\.\. OK/)
67
+ expect(runner).to say(/Binding #{subdomain}\..* to #{app}\.\.\. OK/)
68
+
69
+ expect(runner).to say "Create services for application?> n"
70
+ runner.send_up_arrow
71
+ expect(runner).not_to say "Domain>"
72
+ runner.send_keys "y"
73
+
74
+ # create a service here
75
+ expect(runner).to say "What kind?>"
76
+ runner.send_keys "dummy n/a"
77
+
78
+ expect(runner).to say "Name?>"
79
+ runner.send_keys service_name
80
+
81
+ expect(runner).to say "Which plan?>"
82
+ runner.send_keys "small"
83
+
84
+ expect(runner).to say /Creating service #{service_name}.*OK/
85
+ expect(runner).to say /Binding .+ to .+ OK/
86
+
87
+ expect(runner).to say "Create another service?> n"
88
+ runner.send_up_arrow
89
+ expect(runner).not_to say "Which plan?>"
90
+ runner.send_up_arrow
91
+ expect(runner).not_to say "Which plan?>"
92
+ runner.send_keys "y"
93
+
94
+ # create a user-provided service here
95
+ expect(runner).to say "What kind?>"
96
+ runner.send_keys "user-provided"
97
+
98
+ expect(runner).to say "Name?>"
99
+ runner.send_keys user_provided_name
100
+
101
+ expect(runner).not_to say "Which plan?>"
102
+ expect(runner).to say "What credential parameters should applications use to connect to this service instance?\n(e.g. hostname, port, password)>"
103
+ runner.send_keys "uri"
104
+
105
+ expect(runner).to say "uri>"
106
+ runner.send_keys "mysql://u:p@example.com:3306/db"
107
+
108
+ expect(runner).to say /Creating service #{user_provided_name}.*OK/
109
+ expect(runner).to say /Binding .+ to .+ OK/
110
+
111
+ expect(runner).to say "Create another service?> n"
112
+ runner.send_keys "n"
113
+
114
+ if runner.expect "Bind other services to application?> n", 15
115
+ runner.send_return
116
+ end
117
+
118
+ expect(runner).to say "Save configuration?> n", 20
119
+ runner.send_return
120
+
121
+ expect(runner).to say "Uploading #{app}... OK", 180
122
+ expect(runner).to say "Preparing to start #{app}... OK", 180
123
+ expect(runner).to say "Checking status of app '#{app}'...", 180
124
+ expect(runner).to say "1 of 1 instances running"
125
+ expect(runner).to say "Push successful! App '#{app}' available at #{subdomain}.cfapps.io", 30
126
+ end
127
+ end
128
+
129
+ BlueShell::Runner.run("#{cf_bin} services") do |runner|
130
+ expect(runner).to say /name\s+service\s+provider\s+version\s+plan\s+bound apps/
131
+ expect(runner).to say /dummy-service-.+?\s+ # name
132
+ dummy\s+ # service
133
+ dummy\s+ # provider
134
+ n\/a\s+ # version
135
+ small\s+ # plan
136
+ #{app} # bound apps
137
+ /x
138
+ end
139
+
140
+ BlueShell::Runner.run("#{cf_bin} unbind-service #{service_name} #{app}") do |runner|
141
+ expect(runner).to say "OK", 20
142
+ end
143
+
144
+ BlueShell::Runner.run("#{cf_bin} set-env #{app} DEVELOP_ON_CLOUD_FOUNDRY all_day_erry_day") do |runner|
145
+ expect(runner).to say "OK"
146
+ end
147
+
148
+ BlueShell::Runner.run("#{cf_bin} env #{app}") do |runner|
149
+ expect(runner).to say "DEVELOP_ON_CLOUD_FOUNDRY: all_day_erry_day"
150
+ end
151
+
152
+ BlueShell::Runner.run("#{cf_bin} unset-env #{app} DEVELOP_ON_CLOUD_FOUNDRY") do |runner|
153
+ expect(runner).to say "OK"
154
+ end
155
+
156
+ BlueShell::Runner.run("#{cf_bin} env #{app}") do |runner|
157
+ expect(runner).not_to say "DEVELOP_ON_CLOUD_FOUNDRY"
158
+ end
159
+
160
+ BlueShell::Runner.run("#{cf_bin} delete #{app}") do |runner|
161
+ expect(runner).to say "Really delete #{app}?>"
162
+ runner.send_keys "y"
163
+ expect(runner).to say "Deleting #{app}... OK"
164
+ end
165
+ end
166
+ end
167
+ else
168
+ $stderr.puts 'Skipping v2 integration specs; please provide necessary environment variables'
169
+ end
@@ -0,0 +1,173 @@
1
+ require "spec_helper"
2
+ require "sinatra"
3
+
4
+ class FakeCloudController < Sinatra::Base
5
+ @requests = []
6
+
7
+ class << self
8
+ attr_accessor :requests
9
+
10
+ def last_request
11
+ requests.last
12
+ end
13
+
14
+ def reset
15
+ requests.clear
16
+ end
17
+ end
18
+
19
+ get '/v2/service_brokers' do
20
+ self.class.requests << request
21
+
22
+ broker_guid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
23
+ body = {
24
+ 'total_results' => 1,
25
+ 'total_pages' => 1,
26
+ 'prev_url' => nil,
27
+ 'next_url' => nil,
28
+ 'resources' => [{
29
+ 'metadata' => {
30
+ 'guid' => broker_guid,
31
+ 'url' => "http://cc.example.com/v2/service_brokers/#{broker_guid}",
32
+ 'created_at' => Time.now,
33
+ 'updated_at' => Time.now,
34
+ },
35
+ 'entity' => {
36
+ 'name' => 'my-custom-service',
37
+ 'broker_url' => 'http://broker.example.com/',
38
+ }
39
+ }]
40
+ }.to_json
41
+ [200, {}, body]
42
+ end
43
+
44
+ post '/v2/service_brokers' do
45
+ self.class.requests << request
46
+
47
+ body = {
48
+ metadata: {
49
+ guid: SecureRandom.uuid
50
+ }
51
+ }.to_json
52
+
53
+ [200, {}, body]
54
+ end
55
+
56
+ delete '/v2/service_brokers/:guid' do
57
+ self.class.requests << request
58
+ 204
59
+ end
60
+
61
+ put '/v2/service_brokers/:guid' do
62
+ self.class.requests << request
63
+ 201
64
+ end
65
+
66
+ get '/responsive' do
67
+ 200
68
+ end
69
+ end
70
+
71
+ describe "Service Brokers" do
72
+ let(:broker_url) { 'http://broker.example.com/' }
73
+ let(:auth_username) { 'me' }
74
+ let(:auth_password) { 'opensesame' }
75
+ let(:last_request) { FakeCloudController.last_request }
76
+
77
+ before do
78
+ thr = Thread.new do
79
+ Rack::Handler::WEBrick.run(FakeCloudController, :Port => 12345, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
80
+ end
81
+
82
+ def responsive?
83
+ begin
84
+ resp = Net::HTTP.start('localhost', 12345) { |http| http.get('/responsive') }
85
+ resp.is_a? Net::HTTPSuccess
86
+ rescue Errno::ECONNREFUSED
87
+ false
88
+ end
89
+ end
90
+
91
+ Timeout.timeout(60) { thr.join(0.1) until responsive? }
92
+
93
+ @homedir = File.join(File.dirname(__FILE__), '..', '..', 'tmp')
94
+ FileUtils.mkdir_p(File.join(@homedir, '.truck'))
95
+
96
+ # cf target
97
+ File.open(File.join(@homedir, '.truck', 'target'), 'w') do |f|
98
+ f.puts 'http://127.0.0.1:12345'
99
+ end
100
+
101
+ # cf login
102
+ File.open(File.join(@homedir, '.truck', 'tokens.yml'), 'w') do |f|
103
+ f.puts <<-YAML
104
+ http://127.0.0.1:8181:
105
+ :version: 2
106
+ :token: bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0MGE0YWJlOS0yYTgxLTQyZGYtODVjNS1kNDdlMzRiOTA0YTEiLCJ1c2VyX2lkIjoiMzI5YmIzYmYtMWU3NS00MGQ3LThhMDItMjI3Y2FjY2IyZGU3Iiwic3ViIjoiMzI5YmIzYmYtMWU3NS00MGQ3LThhMDItMjI3Y2FjY2IyZGU3IiwidXNlcl9uYW1lIjoiYWRtaW4iLCJlbWFpbCI6ImFkbWluIiwic2NvcGUiOlsiY2xvdWRfY29udHJvbGxlci5hZG1pbiIsImNsb3VkX2NvbnRyb2xsZXIucmVhZCIsImNsb3VkX2NvbnRyb2xsZXIud3JpdGUiLCJvcGVuaWQiLCJwYXNzd29yZC53cml0ZSIsInNjaW0ucmVhZCIsInNjaW0ud3JpdGUiXSwiY2xpZW50X2lkIjoiY2YiLCJjaWQiOiJjZiIsImlhdCI6MTM3NjU5MzI3NiwiZXhwIjoxMzc2NjAwNDc2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvdWFhL29hdXRoL3Rva2VuIiwiYXVkIjpbInNjaW0iLCJvcGVuaWQiLCJjbG91ZF9jb250cm9sbGVyIiwicGFzc3dvcmQiXX0.VSPhFetKLDvZal8bOK38uTBbkrDD2_IdSjHqluk1WIY
107
+ :refresh_token: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI1MzA3ZGJmNC1iMTE1LTQ0MzgtOWRkNi00ZGVlMDVkZjgwN2IiLCJzdWIiOiIzMjliYjNiZi0xZTc1LTQwZDctOGEwMi0yMjdjYWNjYjJkZTciLCJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImNsb3VkX2NvbnRyb2xsZXIuYWRtaW4iLCJjbG91ZF9jb250cm9sbGVyLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicGFzc3dvcmQud3JpdGUiLCJzY2ltLnJlYWQiLCJzY2ltLndyaXRlIl0sImlhdCI6MTM3NjU5MzI3NiwiZXhwIjoxMzc3ODAyODc2LCJjaWQiOiJjZiIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC91YWEvb2F1dGgvdG9rZW4iLCJhdWQiOlsiY2xvdWRfY29udHJvbGxlci5hZG1pbiIsImNsb3VkX2NvbnRyb2xsZXIucmVhZCIsImNsb3VkX2NvbnRyb2xsZXIud3JpdGUiLCJvcGVuaWQiLCJwYXNzd29yZC53cml0ZSIsInNjaW0ucmVhZCIsInNjaW0ud3JpdGUiXX0.7wlsnXoBuE-jSYTBZboy1U26NWs4-VHBEYYGMLz5YFQ
108
+ :space: 671847d5-9754-49b2-bc9f-977ee42c7e4c
109
+ :organization: 57944537-4f45-424d-a8af-b41ab6b4f0a0
110
+ YAML
111
+ end
112
+ end
113
+
114
+ after do
115
+ crash_log = File.join(@homedir, '.truck', 'crash')
116
+ if File.exists?(crash_log)
117
+ puts `cat #{crash_log}`
118
+ FileUtils.rm(crash_log)
119
+ end
120
+
121
+ FakeCloudController.reset
122
+ end
123
+
124
+ it "allows an admin user to add a service broker" do
125
+ BlueShell::Runner.run("env HOME=#{@homedir} #{cf_bin} add-service-broker --name my-custom-service --url #{broker_url} --username #{auth_username} --password #{auth_password}") do |runner|
126
+ expect(runner).to say "Adding service broker my-custom-service... OK"
127
+ end
128
+
129
+ expect(last_request).to be_post
130
+ expect(last_request.path).to eq('/v2/service_brokers')
131
+ expect(JSON.load(last_request.body)).to eq(
132
+ 'name' => 'my-custom-service',
133
+ 'broker_url' => broker_url,
134
+ 'auth_username' => auth_username,
135
+ 'auth_password' => auth_password,
136
+ )
137
+ end
138
+
139
+ it "allows an admin user to list service brokers" do
140
+ BlueShell::Runner.run("env HOME=#{@homedir} #{cf_bin} service-brokers") do |runner|
141
+ expect(runner).to say /my-custom-service.*#{broker_url}/
142
+ end
143
+
144
+ expect(last_request).to be_get
145
+ expect(last_request.path).to eq('/v2/service_brokers')
146
+ end
147
+
148
+ it "allows an admin user to remove a service broker" do
149
+ BlueShell::Runner.run("env HOME=#{@homedir} #{cf_bin} remove-service-broker my-custom-service") do |runner|
150
+ expect(runner).to say "Really remove my-custom-service?> n"
151
+ runner.send_keys("y")
152
+ expect(runner).to say "Removing service broker my-custom-service... OK"
153
+ end
154
+
155
+ expect(last_request).to be_delete
156
+ expect(last_request.path).to eq('/v2/service_brokers/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
157
+ end
158
+
159
+ it "allows an admin user to update a service broker" do
160
+ BlueShell::Runner.run("env HOME=#{@homedir} #{cf_bin} update-service-broker my-custom-service --name cf-othersql --url http://other.example.com/ --username newusername --password newpassword") do |runner|
161
+ expect(runner).to say "Updating service broker my-custom-service... OK"
162
+ end
163
+
164
+ expect(last_request).to be_put
165
+ expect(last_request.path).to eq('/v2/service_brokers/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
166
+ expect(JSON.load(last_request.body)).to eq(
167
+ 'name' => 'cf-othersql',
168
+ 'broker_url' => 'http://other.example.com/',
169
+ 'auth_username' => 'newusername',
170
+ 'auth_password' => 'newpassword',
171
+ )
172
+ end
173
+ end