ncfoundry 4.9.2

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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +11 -0
  8. data/Checkfile +2 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +987 -0
  11. data/NOTICE +10 -0
  12. data/README.md +9 -0
  13. data/Rakefile +14 -0
  14. data/VERSION +1 -0
  15. data/cfoundry.gemspec +41 -0
  16. data/config/locales/en.yml +168 -0
  17. data/lib/cc_api_stub/app_usage_events.rb +11 -0
  18. data/lib/cc_api_stub/applications.rb +57 -0
  19. data/lib/cc_api_stub/domains.rb +32 -0
  20. data/lib/cc_api_stub/events.rb +11 -0
  21. data/lib/cc_api_stub/frameworks.rb +22 -0
  22. data/lib/cc_api_stub/helper.rb +148 -0
  23. data/lib/cc_api_stub/login.rb +21 -0
  24. data/lib/cc_api_stub/organization_users.rb +27 -0
  25. data/lib/cc_api_stub/organizations.rb +82 -0
  26. data/lib/cc_api_stub/routes.rb +26 -0
  27. data/lib/cc_api_stub/runtimes.rb +22 -0
  28. data/lib/cc_api_stub/service_bindings.rb +22 -0
  29. data/lib/cc_api_stub/service_instances.rb +22 -0
  30. data/lib/cc_api_stub/services.rb +21 -0
  31. data/lib/cc_api_stub/space_users.rb +27 -0
  32. data/lib/cc_api_stub/spaces.rb +58 -0
  33. data/lib/cc_api_stub/users.rb +85 -0
  34. data/lib/cc_api_stub.rb +20 -0
  35. data/lib/cfoundry/auth_token.rb +62 -0
  36. data/lib/cfoundry/baseclient.rb +220 -0
  37. data/lib/cfoundry/chatty_hash.rb +46 -0
  38. data/lib/cfoundry/client.rb +42 -0
  39. data/lib/cfoundry/concerns/login_helpers.rb +14 -0
  40. data/lib/cfoundry/concerns/proxy_options.rb +14 -0
  41. data/lib/cfoundry/errors.rb +163 -0
  42. data/lib/cfoundry/rest_client.rb +316 -0
  43. data/lib/cfoundry/test_support.rb +3 -0
  44. data/lib/cfoundry/trace_helpers.rb +64 -0
  45. data/lib/cfoundry/uaaclient.rb +151 -0
  46. data/lib/cfoundry/upload_helpers.rb +222 -0
  47. data/lib/cfoundry/v2/app.rb +313 -0
  48. data/lib/cfoundry/v2/app_event.rb +13 -0
  49. data/lib/cfoundry/v2/app_instance.rb +74 -0
  50. data/lib/cfoundry/v2/app_usage_event.rb +14 -0
  51. data/lib/cfoundry/v2/base.rb +112 -0
  52. data/lib/cfoundry/v2/client.rb +104 -0
  53. data/lib/cfoundry/v2/domain.rb +20 -0
  54. data/lib/cfoundry/v2/event.rb +17 -0
  55. data/lib/cfoundry/v2/helper.rb +11 -0
  56. data/lib/cfoundry/v2/managed_service_instance.rb +13 -0
  57. data/lib/cfoundry/v2/model.rb +213 -0
  58. data/lib/cfoundry/v2/model_magic/attribute.rb +49 -0
  59. data/lib/cfoundry/v2/model_magic/client_extensions.rb +170 -0
  60. data/lib/cfoundry/v2/model_magic/has_summary.rb +49 -0
  61. data/lib/cfoundry/v2/model_magic/query_multi_value_helper.rb +21 -0
  62. data/lib/cfoundry/v2/model_magic/query_value_helper.rb +33 -0
  63. data/lib/cfoundry/v2/model_magic/queryable_by.rb +39 -0
  64. data/lib/cfoundry/v2/model_magic/to_many.rb +145 -0
  65. data/lib/cfoundry/v2/model_magic/to_one.rb +81 -0
  66. data/lib/cfoundry/v2/model_magic.rb +134 -0
  67. data/lib/cfoundry/v2/organization.rb +44 -0
  68. data/lib/cfoundry/v2/quota_definition.rb +12 -0
  69. data/lib/cfoundry/v2/route.rb +25 -0
  70. data/lib/cfoundry/v2/service.rb +22 -0
  71. data/lib/cfoundry/v2/service_auth_token.rb +9 -0
  72. data/lib/cfoundry/v2/service_binding.rb +10 -0
  73. data/lib/cfoundry/v2/service_broker.rb +12 -0
  74. data/lib/cfoundry/v2/service_instance.rb +14 -0
  75. data/lib/cfoundry/v2/service_plan.rb +16 -0
  76. data/lib/cfoundry/v2/space.rb +31 -0
  77. data/lib/cfoundry/v2/stack.rb +10 -0
  78. data/lib/cfoundry/v2/user.rb +97 -0
  79. data/lib/cfoundry/v2/user_provided_service_instance.rb +16 -0
  80. data/lib/cfoundry/validator.rb +41 -0
  81. data/lib/cfoundry/version.rb +4 -0
  82. data/lib/cfoundry/zip.rb +56 -0
  83. data/lib/cfoundry.rb +6 -0
  84. data/release_notes/release_1_5_3.md +177 -0
  85. data/release_notes/release_2.3.1.md +14 -0
  86. data/release_notes/release_2.3.3.md +38 -0
  87. data/release_notes/release_2.3.4.md +16 -0
  88. data/release_notes/release_2.3.5.md +14 -0
  89. data/release_notes/release_2.3.6.md +14 -0
  90. data/release_notes/release_2.3.6.rc1.md +29 -0
  91. data/release_notes/release_2.3.6.rc2.md +19 -0
  92. data/release_notes/release_2.3.7.rc1.md +14 -0
  93. data/release_notes/release_2.4.0.md +41 -0
  94. data/release_notes/release_2.4.1.rc1.md +58 -0
  95. data/release_notes/release_3.0.0.md +21 -0
  96. data/release_notes/release_3.0.1.md +16 -0
  97. data/release_notes/release_3.0.2.rc1.md +19 -0
  98. data/release_notes/release_4.0.0.md +19 -0
  99. data/release_notes/release_4.0.1.md +14 -0
  100. data/release_notes/release_4.0.2.rc1.md +14 -0
  101. data/release_notes/release_4.0.2.rc2.md +11 -0
  102. data/release_notes/release_4.0.2.rc3.md +11 -0
  103. data/release_notes/release_4.0.2.rc4.md +19 -0
  104. data/release_notes/release_4.0.2.rc5.md +14 -0
  105. data/release_notes/release_4.0.3.md +19 -0
  106. data/release_notes/release_4.0.4.rc1.md +14 -0
  107. data/release_notes/release_4.0.4.rc2.md +14 -0
  108. data/release_notes/release_4.1.0.md +19 -0
  109. data/release_notes/release_4.2.0.rc.md +24 -0
  110. data/release_notes/release_4.3.0.md +22 -0
  111. data/release_notes/release_4.3.1.md +14 -0
  112. data/release_notes/release_4.3.10.md +14 -0
  113. data/release_notes/release_4.3.11.md +14 -0
  114. data/release_notes/release_4.3.12.md +14 -0
  115. data/release_notes/release_4.3.2.rc1.md +21 -0
  116. data/release_notes/release_4.3.3.md +24 -0
  117. data/release_notes/release_4.3.4.md +11 -0
  118. data/release_notes/release_4.3.4.rc1.md +24 -0
  119. data/release_notes/release_4.3.5.md +36 -0
  120. data/release_notes/release_4.3.5.rc1.md +21 -0
  121. data/release_notes/release_4.3.6.md +14 -0
  122. data/release_notes/release_4.3.7.md +50 -0
  123. data/release_notes/release_4.3.8.md +19 -0
  124. data/release_notes/release_4.3.9.md +14 -0
  125. data/release_notes/release_4.4.0.md +16 -0
  126. data/release_notes/release_4.5.1.md +11 -0
  127. data/release_notes/release_4.5.2.md +14 -0
  128. data/release_notes/release_4.5.3.md +29 -0
  129. data/release_notes/release_4.6.0.md +24 -0
  130. data/release_notes/release_4.6.1.md +14 -0
  131. data/release_notes/release_4.6.2.md +16 -0
  132. data/release_notes/release_4.6.3.rc1.md +14 -0
  133. data/release_notes/release_4.6.3.rc2.md +14 -0
  134. data/release_notes/release_4.6.3.rc3.md +17 -0
  135. data/release_notes/release_4.7.0.md +11 -0
  136. data/release_notes/release_4.7.1.md +14 -0
  137. data/release_notes/release_4.7.1.rc.1.md +14 -0
  138. data/release_notes/release_4.7.1.rc1.md +19 -0
  139. data/script/gpp +3 -0
  140. data/spec/cc_api_stub/app_usage_events_spec.rb +12 -0
  141. data/spec/cc_api_stub/applications_spec.rb +69 -0
  142. data/spec/cc_api_stub/domains_spec.rb +40 -0
  143. data/spec/cc_api_stub/events_spec.rb +12 -0
  144. data/spec/cc_api_stub/frameworks_spec.rb +19 -0
  145. data/spec/cc_api_stub/login_spec.rb +20 -0
  146. data/spec/cc_api_stub/organization_users_spec.rb +35 -0
  147. data/spec/cc_api_stub/organizations_spec.rb +118 -0
  148. data/spec/cc_api_stub/routes_spec.rb +19 -0
  149. data/spec/cc_api_stub/runtimes_spec.rb +19 -0
  150. data/spec/cc_api_stub/service_bindings_spec.rb +13 -0
  151. data/spec/cc_api_stub/service_instances_spec.rb +19 -0
  152. data/spec/cc_api_stub/services_spec.rb +11 -0
  153. data/spec/cc_api_stub/space_users_spec.rb +35 -0
  154. data/spec/cc_api_stub/spaces_spec.rb +38 -0
  155. data/spec/cc_api_stub/users_spec.rb +107 -0
  156. data/spec/cfoundry/auth_token_spec.rb +153 -0
  157. data/spec/cfoundry/baseclient_spec.rb +284 -0
  158. data/spec/cfoundry/client_spec.rb +13 -0
  159. data/spec/cfoundry/errors_spec.rb +117 -0
  160. data/spec/cfoundry/rest_client_spec.rb +367 -0
  161. data/spec/cfoundry/trace_helpers_spec.rb +91 -0
  162. data/spec/cfoundry/uaaclient_spec.rb +421 -0
  163. data/spec/cfoundry/upload_helpers_spec.rb +182 -0
  164. data/spec/cfoundry/v2/app_event_spec.rb +97 -0
  165. data/spec/cfoundry/v2/app_instance_spec.rb +31 -0
  166. data/spec/cfoundry/v2/app_spec.rb +354 -0
  167. data/spec/cfoundry/v2/app_usage_event_spec.rb +15 -0
  168. data/spec/cfoundry/v2/base_spec.rb +375 -0
  169. data/spec/cfoundry/v2/client_spec.rb +121 -0
  170. data/spec/cfoundry/v2/domain_spec.rb +63 -0
  171. data/spec/cfoundry/v2/event_spec.rb +15 -0
  172. data/spec/cfoundry/v2/managed_service_instance_spec.rb +149 -0
  173. data/spec/cfoundry/v2/model_magic/attribute_spec.rb +123 -0
  174. data/spec/cfoundry/v2/model_magic/has_summary_spec.rb +17 -0
  175. data/spec/cfoundry/v2/model_magic/to_many_spec.rb +53 -0
  176. data/spec/cfoundry/v2/model_magic/to_one_spec.rb +106 -0
  177. data/spec/cfoundry/v2/model_magic_spec.rb +43 -0
  178. data/spec/cfoundry/v2/model_spec.rb +472 -0
  179. data/spec/cfoundry/v2/organization_spec.rb +282 -0
  180. data/spec/cfoundry/v2/quota_definition_spec.rb +50 -0
  181. data/spec/cfoundry/v2/route_spec.rb +42 -0
  182. data/spec/cfoundry/v2/service_plan_spec.rb +53 -0
  183. data/spec/cfoundry/v2/service_spec.rb +58 -0
  184. data/spec/cfoundry/v2/space_spec.rb +160 -0
  185. data/spec/cfoundry/v2/user_provided_service_instance_spec.rb +57 -0
  186. data/spec/cfoundry/v2/user_spec.rb +206 -0
  187. data/spec/cfoundry/validator_spec.rb +94 -0
  188. data/spec/factories/app_events_factory.rb +7 -0
  189. data/spec/factories/app_usage_events_factory.rb +32 -0
  190. data/spec/factories/apps_factory.rb +11 -0
  191. data/spec/factories/clients_factory.rb +7 -0
  192. data/spec/factories/domains_factory.rb +10 -0
  193. data/spec/factories/events_factory.rb +50 -0
  194. data/spec/factories/organizations_factory.rb +12 -0
  195. data/spec/factories/quota_definitions_factory.rb +8 -0
  196. data/spec/factories/routes_factory.rb +10 -0
  197. data/spec/factories/service_instances_factory.rb +10 -0
  198. data/spec/factories/service_plans_factory.rb +10 -0
  199. data/spec/factories/services_factory.rb +10 -0
  200. data/spec/factories/spaces_factory.rb +10 -0
  201. data/spec/factories/user_provided_service_instances_factory.rb +10 -0
  202. data/spec/factories/users_factory.rb +10 -0
  203. data/spec/fixtures/apps/with_cfignore/.cfignore +4 -0
  204. data/spec/fixtures/apps/with_cfignore/.hidden_file +1 -0
  205. data/spec/fixtures/apps/with_cfignore/ambiguous_ignored +0 -0
  206. data/spec/fixtures/apps/with_cfignore/ignored_dir/file_in_ignored_dir.txt +1 -0
  207. data/spec/fixtures/apps/with_cfignore/ignored_file.txt +1 -0
  208. data/spec/fixtures/apps/with_cfignore/non_ignored_dir/file_in_non_ignored_dir.txt +1 -0
  209. data/spec/fixtures/apps/with_cfignore/non_ignored_dir/ignored_file.txt +1 -0
  210. data/spec/fixtures/apps/with_cfignore/non_ignored_dir/toplevel_ignored.txt +0 -0
  211. data/spec/fixtures/apps/with_cfignore/non_ignored_file.txt +1 -0
  212. data/spec/fixtures/apps/with_cfignore/toplevel_ignored.txt +0 -0
  213. data/spec/fixtures/apps/with_dotfiles/.dotfile +1 -0
  214. data/spec/fixtures/apps/with_dotfiles/xyz +1 -0
  215. data/spec/fixtures/apps/with_external_symlink/foo +1 -0
  216. data/spec/fixtures/apps/with_ignored_external_symlink/.cfignore +1 -0
  217. data/spec/fixtures/apps/with_ignored_external_symlink/foo +1 -0
  218. data/spec/fixtures/apps/with_nested_directories/foo/bar/baz/fizz +0 -0
  219. data/spec/fixtures/apps/with_nested_directories/xyz +0 -0
  220. data/spec/fixtures/empty_file +0 -0
  221. data/spec/fixtures/fake_cc_app_usage_events.json +152 -0
  222. data/spec/fixtures/fake_cc_application.json +20 -0
  223. data/spec/fixtures/fake_cc_application_summary.json +56 -0
  224. data/spec/fixtures/fake_cc_created_application.json +11 -0
  225. data/spec/fixtures/fake_cc_created_domain.json +15 -0
  226. data/spec/fixtures/fake_cc_created_organization.json +11 -0
  227. data/spec/fixtures/fake_cc_created_route.json +13 -0
  228. data/spec/fixtures/fake_cc_created_service_instance.json +11 -0
  229. data/spec/fixtures/fake_cc_created_space.json +11 -0
  230. data/spec/fixtures/fake_cc_created_user.json +11 -0
  231. data/spec/fixtures/fake_cc_domain.json +55 -0
  232. data/spec/fixtures/fake_cc_domain_spaces.json +27 -0
  233. data/spec/fixtures/fake_cc_empty_search.json +7 -0
  234. data/spec/fixtures/fake_cc_events.json +419 -0
  235. data/spec/fixtures/fake_cc_frameworks.json +20 -0
  236. data/spec/fixtures/fake_cc_managed_service_instance.json +83 -0
  237. data/spec/fixtures/fake_cc_organization.json +161 -0
  238. data/spec/fixtures/fake_cc_organization_domains.json +59 -0
  239. data/spec/fixtures/fake_cc_organization_search.json +37 -0
  240. data/spec/fixtures/fake_cc_organization_spaces.json +99 -0
  241. data/spec/fixtures/fake_cc_organization_summary.json +20 -0
  242. data/spec/fixtures/fake_cc_organization_users.json +81 -0
  243. data/spec/fixtures/fake_cc_route.json +16 -0
  244. data/spec/fixtures/fake_cc_runtimes.json +20 -0
  245. data/spec/fixtures/fake_cc_service_binding.json +22 -0
  246. data/spec/fixtures/fake_cc_service_bindings.json +24 -0
  247. data/spec/fixtures/fake_cc_service_instance.json +83 -0
  248. data/spec/fixtures/fake_cc_service_instances.json +72 -0
  249. data/spec/fixtures/fake_cc_services.json +160 -0
  250. data/spec/fixtures/fake_cc_space.json +45 -0
  251. data/spec/fixtures/fake_cc_space_apps.json +49 -0
  252. data/spec/fixtures/fake_cc_space_summary.json +84 -0
  253. data/spec/fixtures/fake_cc_spaces.json +92 -0
  254. data/spec/fixtures/fake_cc_stats.json +29 -0
  255. data/spec/fixtures/fake_cc_user.json +139 -0
  256. data/spec/fixtures/fake_cc_user_organizations.json +92 -0
  257. data/spec/fixtures/fake_cc_user_provided_service_instance.json +51 -0
  258. data/spec/fixtures/fake_cc_user_with_managers.json +85 -0
  259. data/spec/integration/client_spec.rb +38 -0
  260. data/spec/spec_helper.rb +22 -0
  261. data/spec/support/factory_girl.rb +6 -0
  262. data/spec/support/shared_examples/cc_api_stub_request_examples.rb +79 -0
  263. data/spec/support/shared_examples/client_login_examples.rb +46 -0
  264. data/spec/support/shared_examples/model_summary_examples.rb +34 -0
  265. data/spec/support/test_model_builder.rb +10 -0
  266. data/vendor/errors/v1.yml +189 -0
  267. data/vendor/errors/v2.yml +845 -0
  268. metadata +712 -0
@@ -0,0 +1,222 @@
1
+ require "tmpdir"
2
+ require "fileutils"
3
+ require "pathname"
4
+ require "digest/sha1"
5
+
6
+ require "cfoundry/zip"
7
+
8
+ module CFoundry
9
+ module UploadHelpers
10
+ # Default paths to exclude from upload payload.
11
+ UPLOAD_EXCLUDE = %w{.git _darcs .svn}
12
+
13
+ # Minimum size for an application payload to bother checking resources.
14
+ RESOURCE_CHECK_LIMIT = 64 * 1024
15
+
16
+ # Upload application's code to target. Do this after #create! and before
17
+ # #start!
18
+ #
19
+ # [path]
20
+ # A path pointing to either a directory, or a .jar, .war, or .zip
21
+ # file.
22
+ #
23
+ # If a .cfignore file is detected under the given path, it will be used
24
+ # to exclude paths from the payload, similar to a .gitignore.
25
+ #
26
+ # [check_resources]
27
+ # If set to `false`, the entire payload will be uploaded
28
+ # without checking the resource cache.
29
+ #
30
+ # Only do this if you know what you're doing.
31
+ def upload(path, check_resources = true)
32
+ unless File.exist? path
33
+ raise CFoundry::Error, "Invalid application path '#{path}'"
34
+ end
35
+
36
+ zipfile = "#{Dir.tmpdir}/#{@guid}.zip"
37
+ tmpdir = "#{Dir.tmpdir}/.cf_#{@guid}_files"
38
+
39
+ FileUtils.rm_f(zipfile)
40
+ FileUtils.rm_rf(tmpdir)
41
+
42
+ prepare_package(path, tmpdir)
43
+
44
+ resources = determine_resources(tmpdir) if check_resources
45
+
46
+ packed = CFoundry::Zip.pack(tmpdir, zipfile)
47
+
48
+ @client.base.upload_app(@guid, packed && zipfile, resources || [])
49
+ ensure
50
+ FileUtils.rm_f(zipfile) if zipfile
51
+ FileUtils.rm_rf(tmpdir) if tmpdir
52
+ end
53
+
54
+ private
55
+
56
+ def prepare_package(path, to)
57
+ if path =~ /\.(jar|war|zip)$/
58
+ CFoundry::Zip.unpack(path, to)
59
+ elsif (war_file = Dir.glob("#{path}/*.war").first)
60
+ CFoundry::Zip.unpack(war_file, to)
61
+ else
62
+ FileUtils.mkdir(to)
63
+
64
+ exclude = UPLOAD_EXCLUDE
65
+ if File.exists?("#{path}/.cfignore")
66
+ exclude += File.read("#{path}/.cfignore").split(/\n+/)
67
+ end
68
+
69
+ files = files_to_consider(path, exclude)
70
+
71
+ check_unreachable_links(files, path)
72
+
73
+ copy_tree(files, path, to)
74
+
75
+ find_sockets(to).each do |s|
76
+ File.delete s
77
+ end
78
+ end
79
+ end
80
+
81
+ def check_unreachable_links(files, path)
82
+ # only used for friendlier error message
83
+ pwd = Pathname.pwd
84
+
85
+ abspath = File.expand_path(path)
86
+ unreachable = []
87
+ files.each do |f|
88
+ file = Pathname.new(f)
89
+ if file.symlink? && !file.realpath.to_s.start_with?(abspath)
90
+ unreachable << file.relative_path_from(pwd)
91
+ end
92
+ end
93
+
94
+ unless unreachable.empty?
95
+ root = Pathname.new(path).relative_path_from(pwd)
96
+ raise CFoundry::Error,
97
+ "Path contains links '#{unreachable}' that point outside '#{root}'"
98
+ end
99
+ end
100
+
101
+ def files_to_consider(path, exclusions)
102
+ entries = all_files(path)
103
+
104
+ exclusions.each do |exclusion|
105
+ ignore_pattern = exclusion.start_with?("/") ? File.join(path, exclusion) : File.join(path, "**", exclusion)
106
+ dir_glob = Dir.glob(ignore_pattern, File::FNM_DOTMATCH)
107
+ entries -= dir_glob
108
+
109
+ ignore_pattern = File.join(path, "**", exclusion, "**", "*")
110
+ dir_glob = Dir.glob(ignore_pattern, File::FNM_DOTMATCH)
111
+ entries -= dir_glob
112
+ end
113
+
114
+ entries
115
+ end
116
+
117
+ def glob_matches?(file, path, pattern)
118
+ name = file.sub("#{path}/", "/")
119
+ flags = File::FNM_DOTMATCH
120
+
121
+ # when pattern ends with /, match only directories
122
+ if pattern.end_with?("/") && File.directory?(file)
123
+ name = "#{name}/"
124
+ end
125
+
126
+ case pattern
127
+ # when pattern contains /, do a pathname match
128
+ when /\/./
129
+ flags |= File::FNM_PATHNAME
130
+
131
+ # otherwise, match any file path
132
+ else
133
+ pattern = "**/#{pattern}"
134
+ end
135
+
136
+ File.fnmatch(pattern, name, flags)
137
+ end
138
+
139
+ def find_sockets(path)
140
+ all_files(path).select { |f| File.socket? f }
141
+ end
142
+
143
+ def determine_resources(path)
144
+ fingerprints, total_size = make_fingerprints(path)
145
+
146
+ return if total_size <= RESOURCE_CHECK_LIMIT
147
+
148
+ resources = @client.base.resource_match(fingerprints)
149
+
150
+ resources.each do |resource|
151
+ FileUtils.rm_f resource[:fn]
152
+ resource[:fn].sub!("#{path}/", "")
153
+ end
154
+
155
+ prune_empty_directories(path)
156
+
157
+ resources
158
+ end
159
+
160
+ # OK, HERES THE PLAN...
161
+ #
162
+ # 1. Get all the directories in the entire file tree.
163
+ # 2. Sort them by the length of their absolute path.
164
+ # 3. Go through the list, longest paths first, and remove
165
+ # the directories that are empty.
166
+ #
167
+ # This ensures that directories containing empty directories
168
+ # are also pruned.
169
+ def prune_empty_directories(path)
170
+ all_files = all_files(path)
171
+
172
+ directories = all_files.select { |x| File.directory?(x) }
173
+ directories.sort! { |a, b| b.size <=> a.size }
174
+
175
+ directories.each do |directory|
176
+ entries = all_files(directory)
177
+ FileUtils.rmdir(directory) if entries.empty?
178
+ end
179
+ end
180
+
181
+ def make_fingerprints(path)
182
+ fingerprints = []
183
+ total_size = 0
184
+
185
+ all_files(path).each do |filename|
186
+ next if File.directory?(filename)
187
+
188
+ size = File.size(filename)
189
+
190
+ total_size += size
191
+
192
+ fingerprints << {
193
+ :size => size,
194
+ :sha1 => Digest::SHA1.file(filename).hexdigest,
195
+ :fn => filename
196
+ }
197
+ end
198
+
199
+ [fingerprints, total_size]
200
+ end
201
+
202
+ def all_files(path)
203
+ Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).reject do |fn|
204
+ fn =~ /\.$/
205
+ end
206
+ end
207
+
208
+ def copy_tree(files, path, to)
209
+ files.each do |file|
210
+ dest = file.sub("#{path}/", "#{to}/")
211
+
212
+ if File.directory?(file)
213
+ FileUtils.mkdir_p(dest)
214
+ else
215
+ destdir = File.dirname(dest)
216
+ FileUtils.mkdir_p(destdir)
217
+ FileUtils.cp(file, dest)
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,313 @@
1
+ require "tmpdir"
2
+ require "multi_json"
3
+
4
+ require "cfoundry/zip"
5
+ require "cfoundry/upload_helpers"
6
+ require "cfoundry/chatty_hash"
7
+
8
+ require "cfoundry/v2/model"
9
+
10
+ module CFoundry::V2
11
+ # Class for representing a user's application on a given target (via
12
+ # Client).
13
+ #
14
+ # Does not guarantee that the app exists; used for both app creation and
15
+ # retrieval, as the attributes are all lazily retrieved. Setting attributes
16
+ # does not perform any requests; use #update! to commit your changes.
17
+ class App < Model
18
+ include CFoundry::UploadHelpers
19
+
20
+ attribute :name, :string
21
+ to_one :space
22
+ attribute :environment_json, :hash, :default => {}
23
+ attribute :memory, :integer, :default => 256
24
+ attribute :total_instances, :integer, :default => 1, :at => :instances
25
+ attribute :disk_quota, :integer, :default => 256
26
+ attribute :state, :string, :default => "STOPPED"
27
+ attribute :command, :string, :default => nil
28
+ attribute :console, :boolean, :default => false
29
+ attribute :buildpack, :string, :default => nil
30
+ to_one :stack, :default => nil
31
+ attribute :debug, :string, :default => nil
32
+ to_many :service_bindings
33
+ to_many :routes
34
+ to_many :events, :as => :app_event
35
+
36
+ scoped_to_space
37
+
38
+ queryable_by :name, :space_guid, :organization_guid
39
+
40
+ has_summary :urls => proc { |x| self.cache[:uris] = x },
41
+ :running_instances => proc { |x|
42
+ self.cache[:running_instances] = x
43
+ },
44
+ :instances => proc { |x|
45
+ self.total_instances = x
46
+ }
47
+
48
+ private :environment_json
49
+
50
+ def delete!(opts = {})
51
+ super(opts.merge(:recursive => true))
52
+ end
53
+
54
+ def instances
55
+ AppInstance.for_app(name, @guid, @client)
56
+ end
57
+
58
+ def crashes
59
+ @client.base.crashes(@guid).collect do |m|
60
+ AppInstance.new(self.name, self.guid, m[:instance], @client, m)
61
+ end
62
+ end
63
+
64
+ def stats
65
+ stats = {}
66
+
67
+ @client.base.stats(@guid).each do |idx, info|
68
+ stats[idx.to_s] = info
69
+ end
70
+
71
+ stats
72
+ end
73
+
74
+ def system_env
75
+ system_env = {}
76
+
77
+ @client.base.env(@guid).each do |idx, info|
78
+ system_env[idx.to_s] = info
79
+ end
80
+
81
+ system_env
82
+ end
83
+
84
+ def services
85
+ service_bindings.collect(&:service_instance)
86
+ end
87
+
88
+ def env
89
+ CFoundry::ChattyHash.new(
90
+ method(:env=),
91
+ stringify(environment_json))
92
+ end
93
+
94
+ def env=(x)
95
+ self.environment_json = stringify(x.to_hash)
96
+ end
97
+
98
+ alias :debug_mode :debug
99
+
100
+ def uris
101
+ return @cache[:uris] if @cache[:uris]
102
+
103
+ routes.collect do |r|
104
+ "#{r.host}.#{r.domain.name}"
105
+ end
106
+ end
107
+ alias :urls :uris
108
+
109
+ def uris=(uris)
110
+ raise CFoundry::Deprecated,
111
+ "App#uris= is invalid against V2 APIs; use add/remove_route"
112
+ end
113
+ alias :urls= :uris=
114
+
115
+ def uri
116
+ if uris = @cache[:uris]
117
+ return uris.first
118
+ end
119
+
120
+ if route = routes.first
121
+ "#{route.host}.#{route.domain.name}"
122
+ end
123
+ end
124
+ alias :url :uri
125
+
126
+ def host
127
+ return nil if routes.empty?
128
+ routes.first.host
129
+ end
130
+
131
+ def domain
132
+ return nil if routes.empty?
133
+ routes.first.domain.name
134
+ end
135
+
136
+ def uri=(x)
137
+ self.uris = [x]
138
+ end
139
+ alias :url= :uri=
140
+
141
+ # Stop the application.
142
+ def stop!
143
+ self.state = "STOPPED"
144
+ update!
145
+ end
146
+
147
+ # Start the application.
148
+ def start!(&blk)
149
+ self.state = "STARTED"
150
+ update!(&blk)
151
+ end
152
+
153
+ # Restart the application.
154
+ def restart!(&blk)
155
+ stop!
156
+ start!(&blk)
157
+ end
158
+
159
+ def update!
160
+ response = @client.base.update_app(@guid, @diff)
161
+
162
+ yield response[:headers]["x-app-staging-log"] if block_given?
163
+
164
+ @manifest = @client.base.send(:parse_json, response[:body])
165
+
166
+ @diff.clear
167
+
168
+ true
169
+ end
170
+
171
+ def stream_update_log(log_url)
172
+ offset = 0
173
+
174
+ while true
175
+ begin
176
+ @client.stream_url(log_url + "&tail&tail_offset=#{offset}") do |out|
177
+ offset += out.size
178
+ yield out
179
+ end
180
+ rescue Timeout::Error
181
+ end
182
+ end
183
+ rescue CFoundry::APIError
184
+ end
185
+
186
+ # Determine application health.
187
+ #
188
+ # If all instances are running, returns "RUNNING". If only some are
189
+ # started, returns the percentage of them that are healthy.
190
+ #
191
+ # Otherwise, returns application's status.
192
+ def health
193
+ if state == "STARTED"
194
+ healthy_count = running_instances
195
+ expected = total_instances
196
+
197
+ if expected > 0
198
+ ratio = healthy_count / expected.to_f
199
+ if ratio == 1.0
200
+ "RUNNING"
201
+ else
202
+ "#{(ratio * 100).to_i}%"
203
+ end
204
+ else
205
+ "N/A"
206
+ end
207
+ else
208
+ state
209
+ end
210
+ rescue CFoundry::StagingError, CFoundry::NotStaged
211
+ "STAGING FAILED"
212
+ end
213
+
214
+ def percent_running
215
+ if state == "STARTED"
216
+ healthy_count = running_instances
217
+ expected = total_instances
218
+
219
+ expected > 0 ? (healthy_count / expected.to_f) * 100 : 0
220
+ else
221
+ 0
222
+ end
223
+ rescue CFoundry::StagingError, CFoundry::NotStaged
224
+ 0
225
+ end
226
+
227
+ def running_instances
228
+ return @cache[:running_instances] if @cache[:running_instances]
229
+
230
+ running = 0
231
+
232
+ instances.each do |i|
233
+ running += 1 if i.state == "RUNNING"
234
+ end
235
+
236
+ running
237
+ end
238
+
239
+ # Check that all application instances are running.
240
+ def healthy?
241
+ # invalidate cache so the check is fresh
242
+ invalidate!
243
+ health == "RUNNING"
244
+ end
245
+ alias_method :running?, :healthy?
246
+
247
+ # Is the application stopped?
248
+ def stopped?
249
+ state == "STOPPED"
250
+ end
251
+
252
+ # Is the application started?
253
+ #
254
+ # Note that this does not imply that all instances are running. See
255
+ # #healthy?
256
+ def started?
257
+ state == "STARTED"
258
+ end
259
+
260
+ # Bind services to application.
261
+ def bind(*instances)
262
+ instances.each do |i|
263
+ binding = @client.service_binding
264
+ binding.app = self
265
+ binding.service_instance = i
266
+ binding.create!
267
+ end
268
+
269
+ self
270
+ end
271
+
272
+ # Unbind services from application.
273
+ def unbind(*instances)
274
+ service_bindings.each do |b|
275
+ if instances.include? b.service_instance
276
+ b.delete!
277
+ end
278
+ end
279
+
280
+ self
281
+ end
282
+
283
+ def binds?(instance)
284
+ service_bindings.any? { |b|
285
+ b.service_instance == instance
286
+ }
287
+ end
288
+
289
+ def files(*path)
290
+ AppInstance.new(self.name, self.guid, "0", @client).files(*path)
291
+ end
292
+
293
+ def file(*path)
294
+ AppInstance.new(self.name, self.guid, "0", @client).file(*path)
295
+ end
296
+
297
+ def stream_file(*path, &blk)
298
+ AppInstance.new(self.name, self.guid, "0", @client).stream_file(*path, &blk)
299
+ end
300
+
301
+ private
302
+
303
+ def stringify(hash)
304
+ new = {}
305
+
306
+ hash.each do |k, v|
307
+ new[k.to_s] = v.to_s
308
+ end
309
+
310
+ new
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,13 @@
1
+ require "cfoundry/v2/model"
2
+
3
+ module CFoundry::V2
4
+ class AppEvent < Model
5
+ to_one :app
6
+
7
+ attribute :instance_guid, :string
8
+ attribute :instance_index, :integer
9
+ attribute :exit_status, :integer
10
+ attribute :exit_description, :string, :default => ""
11
+ attribute :timestamp, :string
12
+ end
13
+ end
@@ -0,0 +1,74 @@
1
+ module CFoundry::V2
2
+ class AppInstance
3
+ attr_reader :id
4
+
5
+ def self.for_app(name, guid, client)
6
+ client.base.instances(guid).collect do |i, m|
7
+ AppInstance.new(name, guid, i.to_s, client, m)
8
+ end
9
+ end
10
+
11
+ def initialize(app_name, app_guid, id, client, manifest = {})
12
+ @app_name = app_name
13
+ @app_guid = app_guid
14
+ @id = id
15
+ @client = client
16
+ @manifest = manifest
17
+ end
18
+
19
+ def inspect
20
+ "#<App::Instance '#{@app_name}' \##@id>"
21
+ end
22
+
23
+ def state
24
+ @manifest[:state]
25
+ end
26
+
27
+ alias_method :status, :state
28
+
29
+ def since
30
+ if since = @manifest[:since]
31
+ Time.at(@manifest[:since])
32
+ end
33
+ end
34
+
35
+ def debugger
36
+ return unless @manifest[:debug_ip] and @manifest[:debug_port]
37
+
38
+ {:ip => @manifest[:debug_ip],
39
+ :port => @manifest[:debug_port]
40
+ }
41
+ end
42
+
43
+ def console
44
+ return unless @manifest[:console_ip] and @manifest[:console_port]
45
+
46
+ {:ip => @manifest[:console_ip],
47
+ :port => @manifest[:console_port]
48
+ }
49
+ end
50
+
51
+ def healthy?
52
+ case state
53
+ when "STARTING", "RUNNING"
54
+ true
55
+ when "DOWN", "FLAPPING"
56
+ false
57
+ end
58
+ end
59
+
60
+ def files(*path)
61
+ @client.base.files(@app_guid, @id, *path).split("\n").collect do |entry|
62
+ path + [entry.split(/\s+/, 2)[0]]
63
+ end
64
+ end
65
+
66
+ def file(*path)
67
+ @client.base.files(@app_guid, @id, *path)
68
+ end
69
+
70
+ def stream_file(*path, &blk)
71
+ @client.base.stream_file(@app_guid, @id, *path, &blk)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,14 @@
1
+ require "cfoundry/v2/model"
2
+
3
+ module CFoundry::V2
4
+ class AppUsageEvent < Model
5
+ attribute :state, :string
6
+ attribute :memory_in_mb_per_instance, :integer
7
+ attribute :instance_count, :integer
8
+ attribute :app_guid, :string
9
+ attribute :app_name, :string
10
+ attribute :space_guid, :string
11
+ attribute :space_name, :string
12
+ attribute :org_guid, :string
13
+ end
14
+ end