new_cfoundry 4.8.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 (269) 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 +40 -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 +63 -0
  36. data/lib/cfoundry/baseclient.rb +216 -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 +17 -0
  41. data/lib/cfoundry/errors.rb +163 -0
  42. data/lib/cfoundry/rest_client.rb +317 -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 +160 -0
  46. data/lib/cfoundry/upload_helpers.rb +222 -0
  47. data/lib/cfoundry/v2/app.rb +303 -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 +108 -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 +12 -0
  57. data/lib/cfoundry/v2/model.rb +211 -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 +138 -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 +30 -0
  77. data/lib/cfoundry/v2/stack.rb +10 -0
  78. data/lib/cfoundry/v2/user.rb +93 -0
  79. data/lib/cfoundry/v2/user_provided_service_instance.rb +15 -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 +5 -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 +154 -0
  157. data/spec/cfoundry/baseclient_spec.rb +298 -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 +349 -0
  161. data/spec/cfoundry/trace_helpers_spec.rb +91 -0
  162. data/spec/cfoundry/uaaclient_spec.rb +435 -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 +434 -0
  179. data/spec/cfoundry/v2/organization_spec.rb +135 -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 +13 -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/README.md +4 -0
  267. data/vendor/errors/v1.yml +189 -0
  268. data/vendor/errors/v2.yml +384 -0
  269. metadata +693 -0
@@ -0,0 +1,160 @@
1
+ require "cfoundry/baseclient"
2
+ require "uaa"
3
+
4
+ module CFoundry
5
+ class UAAClient
6
+ attr_accessor :target, :client_id, :token, :trace, :http_proxy, :https_proxy
7
+
8
+ def initialize(target, client_id = "cf", options = {})
9
+ @target = target
10
+ @client_id = client_id
11
+ @http_proxy = options[:http_proxy]
12
+ @https_proxy = options[:https_proxy]
13
+ @uaa_info_client = uaa_info_client_for(target)
14
+ end
15
+
16
+ def prompts
17
+ wrap_uaa_errors do
18
+ @uaa_info_client.server[:prompts]
19
+ end
20
+ end
21
+
22
+ def authorize(credentials)
23
+ wrap_uaa_errors do
24
+ authenticate_with_password_grant(credentials) ||
25
+ authenticate_with_implicit_grant(credentials)
26
+ end
27
+ end
28
+
29
+ def user(guid)
30
+ wrap_uaa_errors do
31
+ scim.get(:user, guid)
32
+ end
33
+ end
34
+
35
+ def users
36
+ wrap_uaa_errors do
37
+ scim.query(:user)
38
+ end
39
+ end
40
+
41
+ def change_password(guid, new, old)
42
+ wrap_uaa_errors do
43
+ scim.change_password(guid, new, old)
44
+ end
45
+ end
46
+
47
+ def password_score(password)
48
+ wrap_uaa_errors do
49
+ response = uaa_info_client_for(uaa_url).password_strength(password)
50
+
51
+ required_score = response[:requiredScore] || 0
52
+ case (response[:score] || 0)
53
+ when 10 then
54
+ :strong
55
+ when required_score..9 then
56
+ :good
57
+ else
58
+ :weak
59
+ end
60
+ end
61
+ end
62
+
63
+ def add_user(email, password, options = {})
64
+ wrap_uaa_errors do
65
+ scim.add(
66
+ :user,
67
+ {:userName => email,
68
+ :emails => [{:value => email}],
69
+ :password => password,
70
+ :name => {:givenName => options[:givenName] || email,
71
+ :familyName => options[:familyName] || email}
72
+ }
73
+ )
74
+ end
75
+ end
76
+
77
+ def delete_user(guid)
78
+ wrap_uaa_errors do
79
+ scim.delete(:user, guid)
80
+ end
81
+ end
82
+
83
+ def try_to_refresh_token!
84
+ wrap_uaa_errors do
85
+ begin
86
+ token_info = token_issuer.refresh_token_grant(token.refresh_token)
87
+ self.token = AuthToken.from_uaa_token_info(token_info)
88
+ rescue CF::UAA::TargetError
89
+ self.token
90
+ end
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def uaa_info_client_for(url)
97
+ CF::UAA::Info.new(url,
98
+ :symbolize_keys => true,
99
+ :http_proxy => http_proxy,
100
+ :https_proxy => https_proxy
101
+ )
102
+ end
103
+
104
+ def token_issuer
105
+ @token_issuer ||= CF::UAA::TokenIssuer.new(target, client_id, nil,
106
+ :symbolize_keys => true,
107
+ :http_proxy => @http_proxy,
108
+ :https_proxy => @https_proxy
109
+ )
110
+ @token_issuer.logger.level = @trace ? Logger::Severity::TRACE : 1
111
+ @token_issuer
112
+ end
113
+
114
+ def scim
115
+ auth_header = token && token.auth_header
116
+ scim = CF::UAA::Scim.new(uaa_url, auth_header, :symbolize_keys => true)
117
+ scim.logger.level = @trace ? Logger::Severity::TRACE : 1
118
+ scim
119
+ end
120
+
121
+ def uaa_url
122
+ @uaa_url ||= @uaa_info_client.discover_uaa
123
+ end
124
+
125
+ def authenticate_with_password_grant(credentials)
126
+ begin
127
+ # Currently owner_password_grant method does not allow
128
+ # non-password based authenticate; so we have cheat a little bit.
129
+ token_issuer.send(:request_token,
130
+ {:grant_type => "password", :scope => nil}.merge(credentials))
131
+ rescue CF::UAA::BadResponse => e
132
+ status_code = e.message[/\d+/] || 400
133
+ raise CFoundry::Denied.new("Authorization failed", status_code)
134
+ rescue CF::UAA::TargetError
135
+ false
136
+ end
137
+ end
138
+
139
+ def authenticate_with_implicit_grant(credentials)
140
+ begin
141
+ token_issuer.implicit_grant_with_creds(credentials)
142
+ rescue CF::UAA::BadResponse => e
143
+ status_code = e.message[/\d+/] || 400
144
+ raise CFoundry::Denied.new("Authorization failed", status_code)
145
+ end
146
+ end
147
+
148
+ def wrap_uaa_errors
149
+ yield
150
+ rescue CF::UAA::BadResponse
151
+ raise CFoundry::BadResponse
152
+ rescue CF::UAA::NotFound
153
+ raise CFoundry::NotFound
154
+ rescue CF::UAA::InvalidToken
155
+ raise CFoundry::Denied
156
+ rescue CF::UAA::TargetError => e
157
+ raise CFoundry::UAAError.new(e.info[:error_description], e.info[:error])
158
+ end
159
+ end
160
+ end
@@ -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,303 @@
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 services
75
+ service_bindings.collect(&:service_instance)
76
+ end
77
+
78
+ def env
79
+ CFoundry::ChattyHash.new(
80
+ method(:env=),
81
+ stringify(environment_json))
82
+ end
83
+
84
+ def env=(x)
85
+ self.environment_json = stringify(x.to_hash)
86
+ end
87
+
88
+ alias :debug_mode :debug
89
+
90
+ def uris
91
+ return @cache[:uris] if @cache[:uris]
92
+
93
+ routes.collect do |r|
94
+ "#{r.host}.#{r.domain.name}"
95
+ end
96
+ end
97
+ alias :urls :uris
98
+
99
+ def uris=(uris)
100
+ raise CFoundry::Deprecated,
101
+ "App#uris= is invalid against V2 APIs; use add/remove_route"
102
+ end
103
+ alias :urls= :uris=
104
+
105
+ def uri
106
+ if uris = @cache[:uris]
107
+ return uris.first
108
+ end
109
+
110
+ if route = routes.first
111
+ "#{route.host}.#{route.domain.name}"
112
+ end
113
+ end
114
+ alias :url :uri
115
+
116
+ def host
117
+ return nil if routes.empty?
118
+ routes.first.host
119
+ end
120
+
121
+ def domain
122
+ return nil if routes.empty?
123
+ routes.first.domain.name
124
+ end
125
+
126
+ def uri=(x)
127
+ self.uris = [x]
128
+ end
129
+ alias :url= :uri=
130
+
131
+ # Stop the application.
132
+ def stop!
133
+ self.state = "STOPPED"
134
+ update!
135
+ end
136
+
137
+ # Start the application.
138
+ def start!(&blk)
139
+ self.state = "STARTED"
140
+ update!(&blk)
141
+ end
142
+
143
+ # Restart the application.
144
+ def restart!(&blk)
145
+ stop!
146
+ start!(&blk)
147
+ end
148
+
149
+ def update!
150
+ response = @client.base.update_app(@guid, @diff)
151
+
152
+ yield response[:headers]["x-app-staging-log"] if block_given?
153
+
154
+ @manifest = @client.base.send(:parse_json, response[:body])
155
+
156
+ @diff.clear
157
+
158
+ true
159
+ end
160
+
161
+ def stream_update_log(log_url)
162
+ offset = 0
163
+
164
+ while true
165
+ begin
166
+ @client.stream_url(log_url + "&tail&tail_offset=#{offset}") do |out|
167
+ offset += out.size
168
+ yield out
169
+ end
170
+ rescue Timeout::Error
171
+ end
172
+ end
173
+ rescue CFoundry::APIError
174
+ end
175
+
176
+ # Determine application health.
177
+ #
178
+ # If all instances are running, returns "RUNNING". If only some are
179
+ # started, returns the percentage of them that are healthy.
180
+ #
181
+ # Otherwise, returns application's status.
182
+ def health
183
+ if state == "STARTED"
184
+ healthy_count = running_instances
185
+ expected = total_instances
186
+
187
+ if expected > 0
188
+ ratio = healthy_count / expected.to_f
189
+ if ratio == 1.0
190
+ "RUNNING"
191
+ else
192
+ "#{(ratio * 100).to_i}%"
193
+ end
194
+ else
195
+ "N/A"
196
+ end
197
+ else
198
+ state
199
+ end
200
+ rescue CFoundry::StagingError, CFoundry::NotStaged
201
+ "STAGING FAILED"
202
+ end
203
+
204
+ def percent_running
205
+ if state == "STARTED"
206
+ healthy_count = running_instances
207
+ expected = total_instances
208
+
209
+ expected > 0 ? (healthy_count / expected.to_f) * 100 : 0
210
+ else
211
+ 0
212
+ end
213
+ rescue CFoundry::StagingError, CFoundry::NotStaged
214
+ 0
215
+ end
216
+
217
+ def running_instances
218
+ return @cache[:running_instances] if @cache[:running_instances]
219
+
220
+ running = 0
221
+
222
+ instances.each do |i|
223
+ running += 1 if i.state == "RUNNING"
224
+ end
225
+
226
+ running
227
+ end
228
+
229
+ # Check that all application instances are running.
230
+ def healthy?
231
+ # invalidate cache so the check is fresh
232
+ invalidate!
233
+ health == "RUNNING"
234
+ end
235
+ alias_method :running?, :healthy?
236
+
237
+ # Is the application stopped?
238
+ def stopped?
239
+ state == "STOPPED"
240
+ end
241
+
242
+ # Is the application started?
243
+ #
244
+ # Note that this does not imply that all instances are running. See
245
+ # #healthy?
246
+ def started?
247
+ state == "STARTED"
248
+ end
249
+
250
+ # Bind services to application.
251
+ def bind(*instances)
252
+ instances.each do |i|
253
+ binding = @client.service_binding
254
+ binding.app = self
255
+ binding.service_instance = i
256
+ binding.create!
257
+ end
258
+
259
+ self
260
+ end
261
+
262
+ # Unbind services from application.
263
+ def unbind(*instances)
264
+ service_bindings.each do |b|
265
+ if instances.include? b.service_instance
266
+ b.delete!
267
+ end
268
+ end
269
+
270
+ self
271
+ end
272
+
273
+ def binds?(instance)
274
+ service_bindings.any? { |b|
275
+ b.service_instance == instance
276
+ }
277
+ end
278
+
279
+ def files(*path)
280
+ AppInstance.new(self.name, self.guid, "0", @client).files(*path)
281
+ end
282
+
283
+ def file(*path)
284
+ AppInstance.new(self.name, self.guid, "0", @client).file(*path)
285
+ end
286
+
287
+ def stream_file(*path, &blk)
288
+ AppInstance.new(self.name, self.guid, "0", @client).stream_file(*path, &blk)
289
+ end
290
+
291
+ private
292
+
293
+ def stringify(hash)
294
+ new = {}
295
+
296
+ hash.each do |k, v|
297
+ new[k.to_s] = v.to_s
298
+ end
299
+
300
+ new
301
+ end
302
+ end
303
+ 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