cf 0.6.1.rc9 → 0.6.1.rc10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/lib/cf.rb +4 -0
  2. data/lib/cf/cli.rb +8 -3
  3. data/lib/cf/version.rb +1 -1
  4. data/lib/console/README.md +16 -0
  5. data/lib/console/console.rb +187 -0
  6. data/lib/console/plugin.rb +30 -0
  7. data/lib/manifests/README.md +13 -0
  8. data/lib/manifests/errors.rb +35 -0
  9. data/lib/manifests/loader.rb +31 -0
  10. data/lib/manifests/loader/builder.rb +39 -0
  11. data/lib/manifests/loader/normalizer.rb +145 -0
  12. data/lib/manifests/loader/resolver.rb +79 -0
  13. data/lib/manifests/manifests.rb +304 -0
  14. data/lib/manifests/plugin.rb +141 -0
  15. data/lib/tunnel/README.md +29 -0
  16. data/lib/tunnel/helper-app/Gemfile +11 -0
  17. data/lib/tunnel/helper-app/Gemfile.lock +48 -0
  18. data/lib/tunnel/helper-app/server.rb +43 -0
  19. data/lib/tunnel/plugin.rb +183 -0
  20. data/lib/tunnel/tunnel.rb +305 -0
  21. data/spec/assets/rails328_ruby187_app/Gemfile +39 -0
  22. data/spec/assets/rails328_ruby187_app/README.rdoc +261 -0
  23. data/spec/assets/rails328_ruby187_app/Rakefile +7 -0
  24. data/spec/assets/rails328_ruby187_app/app/assets/images/rails.png +0 -0
  25. data/spec/assets/rails328_ruby187_app/app/assets/javascripts/application.js +15 -0
  26. data/spec/assets/rails328_ruby187_app/app/assets/stylesheets/application.css +13 -0
  27. data/spec/assets/rails328_ruby187_app/app/controllers/application_controller.rb +3 -0
  28. data/spec/assets/rails328_ruby187_app/app/helpers/application_helper.rb +2 -0
  29. data/spec/assets/rails328_ruby187_app/app/views/layouts/application.html.erb +14 -0
  30. data/spec/assets/rails328_ruby187_app/config.ru +4 -0
  31. data/spec/assets/rails328_ruby187_app/config/application.rb +62 -0
  32. data/spec/assets/rails328_ruby187_app/config/boot.rb +6 -0
  33. data/spec/assets/rails328_ruby187_app/config/database.yml +25 -0
  34. data/spec/assets/rails328_ruby187_app/config/environment.rb +5 -0
  35. data/spec/assets/rails328_ruby187_app/config/environments/development.rb +37 -0
  36. data/spec/assets/rails328_ruby187_app/config/environments/production.rb +67 -0
  37. data/spec/assets/rails328_ruby187_app/config/environments/test.rb +37 -0
  38. data/spec/assets/rails328_ruby187_app/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/assets/rails328_ruby187_app/config/initializers/inflections.rb +15 -0
  40. data/spec/assets/rails328_ruby187_app/config/initializers/mime_types.rb +5 -0
  41. data/spec/assets/rails328_ruby187_app/config/initializers/secret_token.rb +7 -0
  42. data/spec/assets/rails328_ruby187_app/config/initializers/session_store.rb +8 -0
  43. data/spec/assets/rails328_ruby187_app/config/initializers/wrap_parameters.rb +14 -0
  44. data/spec/assets/rails328_ruby187_app/config/locales/en.yml +5 -0
  45. data/spec/assets/rails328_ruby187_app/config/routes.rb +58 -0
  46. data/spec/assets/rails328_ruby187_app/db/seeds.rb +7 -0
  47. data/spec/assets/rails328_ruby187_app/doc/README_FOR_APP +2 -0
  48. data/spec/assets/rails328_ruby187_app/manifest.yml +7 -0
  49. data/spec/assets/rails328_ruby187_app/public/404.html +26 -0
  50. data/spec/assets/rails328_ruby187_app/public/422.html +26 -0
  51. data/spec/assets/rails328_ruby187_app/public/500.html +25 -0
  52. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css +0 -0
  53. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css.gz +0 -0
  54. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js +17 -0
  55. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js.gz +0 -0
  56. data/spec/assets/rails328_ruby187_app/public/assets/application.css +0 -0
  57. data/spec/assets/rails328_ruby187_app/public/assets/application.css.gz +0 -0
  58. data/spec/assets/rails328_ruby187_app/public/assets/application.js +17 -0
  59. data/spec/assets/rails328_ruby187_app/public/assets/application.js.gz +0 -0
  60. data/spec/assets/rails328_ruby187_app/public/assets/manifest.yml +4 -0
  61. data/spec/assets/rails328_ruby187_app/public/assets/rails-be8732dac73d845ac5b142c8fb5f9fb0.png +0 -0
  62. data/spec/assets/rails328_ruby187_app/public/assets/rails.png +0 -0
  63. data/spec/assets/rails328_ruby187_app/public/favicon.ico +0 -0
  64. data/spec/assets/rails328_ruby187_app/public/index.html +241 -0
  65. data/spec/assets/rails328_ruby187_app/public/robots.txt +5 -0
  66. data/spec/assets/rails328_ruby187_app/script/rails +6 -0
  67. data/spec/assets/rails328_ruby187_app/test/performance/browsing_test.rb +12 -0
  68. data/spec/assets/rails328_ruby187_app/test/test_helper.rb +13 -0
  69. data/spec/cf/cli/app/stats_spec.rb +20 -20
  70. data/spec/cf/cli_spec.rb +37 -18
  71. data/spec/console/console_spec.rb +189 -0
  72. data/spec/features/push_flow_spec.rb +1 -1
  73. data/spec/manifests/errors_spec.rb +43 -0
  74. data/spec/manifests/loader/builder_spec.rb +80 -0
  75. data/spec/manifests/loader/normalizer_spec.rb +158 -0
  76. data/spec/manifests/manifests_spec.rb +309 -0
  77. data/spec/manifests/plugin_spec.rb +362 -0
  78. data/spec/tunnel/plugin_spec.rb +31 -0
  79. metadata +184 -25
@@ -0,0 +1,79 @@
1
+ module CFManifests
2
+ module Resolver
3
+ def resolve(manifest, resolver)
4
+ new = {}
5
+
6
+ new[:applications] = manifest[:applications].collect do |app|
7
+ resolve_lexically(resolver, app, [manifest])
8
+ end
9
+
10
+ resolve_lexically(resolver, new, [new])
11
+ end
12
+
13
+ private
14
+
15
+ # resolve symbols, with hashes introducing new lexical symbols
16
+ def resolve_lexically(resolver, val, ctx)
17
+ case val
18
+ when Hash
19
+ new = {}
20
+
21
+ val.each do |k, v|
22
+ new[k] = resolve_lexically(resolver, v, [val] + ctx)
23
+ end
24
+
25
+ new
26
+ when Array
27
+ val.collect do |v|
28
+ resolve_lexically(resolver, v, ctx)
29
+ end
30
+ when String
31
+ val.gsub(/\$\{([^\}]+)\}/) do
32
+ resolve_symbol(resolver, $1, ctx)
33
+ end
34
+ else
35
+ val
36
+ end
37
+ end
38
+
39
+ # resolve a symbol to its value, and then resolve that value
40
+ def resolve_symbol(resolver, sym, ctx)
41
+ if found = find_symbol(sym.to_sym, ctx)
42
+ resolve_lexically(resolver, found, ctx)
43
+ found
44
+ elsif dynamic = resolver.resolve_symbol(sym)
45
+ dynamic
46
+ else
47
+ fail("Unknown symbol in manifest: #{sym}")
48
+ end
49
+ end
50
+
51
+ # search for a symbol introduced in the lexical context
52
+ def find_symbol(sym, ctx)
53
+ ctx.each do |h|
54
+ if val = resolve_in(h, sym)
55
+ return val
56
+ end
57
+ end
58
+
59
+ nil
60
+ end
61
+
62
+ # find a value, searching in explicit properties first
63
+ def resolve_in(hash, *where)
64
+ find_in_hash(hash, [:properties] + where) ||
65
+ find_in_hash(hash, where)
66
+ end
67
+
68
+ # helper for following a path of values in a hash
69
+ def find_in_hash(hash, where)
70
+ what = hash
71
+ where.each do |x|
72
+ return nil unless what.is_a?(Hash)
73
+ what = what[x]
74
+ end
75
+
76
+ what
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,304 @@
1
+ require "yaml"
2
+ require "set"
3
+
4
+ require "manifests/loader"
5
+
6
+ module CFManifests
7
+ MANIFEST_FILE = "manifest.yml"
8
+
9
+ @@showed_manifest_usage = false
10
+
11
+ def manifest
12
+ return @manifest if @manifest
13
+
14
+ if manifest_file && File.exists?(manifest_file)
15
+ @manifest = load_manifest(manifest_file)
16
+ end
17
+ end
18
+
19
+ def save_manifest(save_to = manifest_file)
20
+ fail "No manifest to save!" unless @manifest
21
+
22
+ File.open(save_to, "w") do |io|
23
+ YAML.dump(@manifest, io)
24
+ end
25
+ end
26
+
27
+ # find the manifest file to work with
28
+ def manifest_file
29
+ return @manifest_file if @manifest_file
30
+
31
+ unless path = input[:manifest]
32
+ where = Dir.pwd
33
+ while true
34
+ if File.exists?(File.join(where, MANIFEST_FILE))
35
+ path = File.join(where, MANIFEST_FILE)
36
+ break
37
+ elsif File.basename(where) == "/"
38
+ path = nil
39
+ break
40
+ else
41
+ where = File.expand_path("../", where)
42
+ end
43
+ end
44
+ end
45
+
46
+ return unless path
47
+
48
+ @manifest_file = File.expand_path(path)
49
+ end
50
+
51
+ # load and resolve a given manifest file
52
+ def load_manifest(file)
53
+ Loader.new(file, self).manifest
54
+ end
55
+
56
+ # dynamic symbol resolution
57
+ def resolve_symbol(sym)
58
+ case sym
59
+ when "target-url"
60
+ client_target
61
+
62
+ when "target-base"
63
+ target_base
64
+
65
+ when "random-word"
66
+ sprintf("%04x", rand(0x0100000))
67
+
68
+ when /^ask (.+)/
69
+ ask($1)
70
+ end
71
+ end
72
+
73
+ # find apps by an identifier, which may be either a tag, a name, or a path
74
+ def find_apps(identifier)
75
+ return [] unless manifest
76
+
77
+ apps = apps_by(:name, identifier)
78
+
79
+ if apps.empty?
80
+ apps = apps_by(:path, from_manifest(identifier))
81
+ end
82
+
83
+ apps
84
+ end
85
+
86
+ # return all the apps described by the manifest
87
+ def all_apps
88
+ manifest[:applications]
89
+ end
90
+
91
+ def current_apps
92
+ manifest[:applications].select do |app|
93
+ next unless app[:path]
94
+ from_manifest(app[:path]) == Dir.pwd
95
+ end
96
+ end
97
+
98
+ # splits the user's input, resolving paths with the manifest,
99
+ # into internal/external apps
100
+ #
101
+ # internal apps are returned as their data in the manifest
102
+ #
103
+ # external apps are the strings that the user gave, to be
104
+ # passed along wholesale to the wrapped command
105
+ def apps_in_manifest(input = nil, use_name = true, &blk)
106
+ names_or_paths =
107
+ if input.has?(:apps)
108
+ # names may be given but be [], which will still cause
109
+ # interaction, so use #direct instead of #[] here
110
+ input.direct(:apps)
111
+ elsif input.has?(:app)
112
+ [input.direct(:app)]
113
+ elsif input.has?(:name)
114
+ [input.direct(:name)]
115
+ else
116
+ []
117
+ end
118
+
119
+ internal = []
120
+ external = []
121
+
122
+ names_or_paths.each do |x|
123
+ if x.is_a?(String)
124
+ if x =~ %r([/\\])
125
+ apps = find_apps(File.expand_path(x))
126
+
127
+ if apps.empty?
128
+ fail("Path #{b(x)} is not present in manifest #{b(relative_manifest_file)}.")
129
+ end
130
+ else
131
+ apps = find_apps(x)
132
+ end
133
+
134
+ if !apps.empty?
135
+ internal += apps
136
+ else
137
+ external << x
138
+ end
139
+ else
140
+ external << x
141
+ end
142
+ end
143
+
144
+ [internal, external]
145
+ end
146
+
147
+ def create_manifest_for(app, path)
148
+ meta = {
149
+ "name" => app.name,
150
+ "memory" => human_size(app.memory * 1024 * 1024, 0),
151
+ "instances" => app.total_instances,
152
+ "url" => app.url ? app.url.sub(target_base, '${target-base}') : "none",
153
+ "path" => path
154
+ }
155
+
156
+ services = app.services
157
+
158
+ unless services.empty?
159
+ meta["services"] = {}
160
+
161
+ services.each do |i|
162
+ p = i.service_plan
163
+ s = p.service
164
+
165
+ meta["services"][i.name] = {
166
+ "label" => s.label,
167
+ "provider" => s.provider,
168
+ "version" => s.version,
169
+ "plan" => p.name
170
+ }
171
+ end
172
+ end
173
+
174
+ if cmd = app.command
175
+ meta["command"] = cmd
176
+ end
177
+
178
+ if buildpack = app.buildpack
179
+ meta["buildpack"] = buildpack
180
+ end
181
+
182
+ meta
183
+ end
184
+
185
+ private
186
+
187
+ def relative_manifest_file
188
+ Pathname.new(manifest_file).relative_path_from(Pathname.pwd)
189
+ end
190
+
191
+ def show_manifest_usage
192
+ return if @@showed_manifest_usage
193
+
194
+ path = relative_manifest_file
195
+ line "Using manifest file #{c(path, :name)}"
196
+ line
197
+
198
+ @@showed_manifest_usage = true
199
+ end
200
+
201
+ def no_apps
202
+ fail "No applications or manifest to operate on."
203
+ end
204
+
205
+ def warn_reset_changes
206
+ line c("Not applying manifest changes without --reset", :warning)
207
+ line "See `cf diff` for more details."
208
+ line
209
+ end
210
+
211
+ def apps_by(attr, val)
212
+ manifest[:applications].select do |info|
213
+ info[attr] == val
214
+ end
215
+ end
216
+
217
+ # expand a path relative to the manifest file's directory
218
+ def from_manifest(path)
219
+ File.expand_path(path, File.dirname(manifest_file))
220
+ end
221
+
222
+
223
+ def ask_to_save(input, app)
224
+ return if manifest_file
225
+ return unless ask("Save configuration?", :default => false)
226
+
227
+ manifest = create_manifest_for(app, input[:path])
228
+
229
+ with_progress("Saving to #{c("manifest.yml", :name)}") do
230
+ File.open("manifest.yml", "w") do |io|
231
+ YAML.dump(
232
+ { "applications" => [manifest] },
233
+ io)
234
+ end
235
+ end
236
+ end
237
+
238
+ def env_hash(val)
239
+ if val.is_a?(Hash)
240
+ val
241
+ else
242
+ hash = {}
243
+
244
+ val.each do |pair|
245
+ name, val = pair.split("=", 2)
246
+ hash[name] = val
247
+ end
248
+
249
+ hash
250
+ end
251
+ end
252
+
253
+ def setup_env(app, info)
254
+ return unless info[:env]
255
+ app.env = env_hash(info[:env])
256
+ end
257
+
258
+ def setup_services(app, info)
259
+ return if !info[:services] || info[:services].empty?
260
+
261
+ offerings = client.services
262
+
263
+ to_bind = []
264
+
265
+ info[:services].each do |name, svc|
266
+ name = name.to_s
267
+
268
+ if instance = client.service_instance_by_name(name)
269
+ to_bind << instance
270
+ else
271
+ offering = offerings.find { |o|
272
+ o.label == (svc[:label] || svc[:type] || svc[:vendor]) &&
273
+ (!svc[:version] || o.version == svc[:version]) &&
274
+ (o.provider == (svc[:provider] || "core"))
275
+ }
276
+
277
+ fail "Unknown service offering: #{svc.inspect}." unless offering
278
+
279
+ plan = offering.service_plans.find { |p|
280
+ p.name == (svc[:plan] || "D100")
281
+ }
282
+
283
+ fail "Unknown service plan: #{svc[:plan]}." unless plan
284
+
285
+ invoke :create_service,
286
+ :name => name,
287
+ :offering => offering,
288
+ :plan => plan,
289
+ :app => app
290
+ end
291
+ end
292
+
293
+ to_bind.each do |s|
294
+ next if app.binds?(s)
295
+
296
+ # TODO: splat
297
+ invoke :bind_service, :app => app, :service => s
298
+ end
299
+ end
300
+
301
+ def target_base
302
+ client_target.sub(/^[^\.]+\./, "")
303
+ end
304
+ end
@@ -0,0 +1,141 @@
1
+ require "pathname"
2
+ require "cf/plugin"
3
+ require "manifests/manifests"
4
+
5
+ class ManifestsPlugin < CF::App::Base
6
+ include CFManifests
7
+
8
+ option :manifest, :aliases => "-m", :value => :file,
9
+ :desc => "Path to manifest file to use"
10
+
11
+
12
+ [ :start, :restart, :instances, :logs, :env, :health, :stats,
13
+ :scale, :app, :stop, :delete
14
+ ].each do |wrap|
15
+ name_made_optional = change_argument(wrap, :app, :optional)
16
+
17
+ around(wrap) do |cmd, input|
18
+ wrap_with_optional_name(name_made_optional, cmd, input)
19
+ end
20
+ end
21
+
22
+
23
+ add_input :push, :reset, :desc => "Reset to values in the manifest",
24
+ :default => false
25
+
26
+ around(:push) do |push, input|
27
+ wrap_push(push, input)
28
+ end
29
+
30
+ private
31
+
32
+ def wrap_with_optional_name(name_made_optional, cmd, input)
33
+ return cmd.call if input[:all]
34
+
35
+ unless manifest
36
+ # if the command knows how to handle this
37
+ if input.has?(:app) || !name_made_optional
38
+ return cmd.call
39
+ else
40
+ return no_apps
41
+ end
42
+ end
43
+
44
+ internal, external = apps_in_manifest(input)
45
+
46
+ return cmd.call if internal.empty? && !external.empty?
47
+
48
+ show_manifest_usage
49
+
50
+ if internal.empty? && external.empty?
51
+ internal = current_apps if internal.empty?
52
+ internal = all_apps if internal.empty?
53
+ end
54
+
55
+ internal = internal.collect { |app| app[:name] }
56
+
57
+ apps = internal + external
58
+ return no_apps if apps.empty?
59
+
60
+ apps.each.with_index do |app, num|
61
+ line unless quiet? || num == 0
62
+ cmd.call(input.without(:apps).merge_given(:app => app))
63
+ end
64
+ end
65
+
66
+ def apply_changes(app, input)
67
+ app.memory = megabytes(input[:memory]) if input.has?(:memory)
68
+ app.total_instances = input[:instances] if input.has?(:instances)
69
+ app.command = input[:command] if input.has?(:command)
70
+ app.production = input[:plan].upcase.start_with?("P") if input.has?(:plan)
71
+ app.buildpack = input[:buildpack] if input.has?(:buildpack)
72
+ end
73
+
74
+ def wrap_push(push, input)
75
+ unless manifest
76
+ create_and_save_manifest(push, input)
77
+ return
78
+ end
79
+
80
+ particular, external = apps_in_manifest(input)
81
+
82
+ unless external.empty?
83
+ fail "Could not find #{b(external.join(", "))}' in the manifest."
84
+ end
85
+
86
+ apps = particular.empty? ? all_apps : particular
87
+
88
+ show_manifest_usage
89
+
90
+ spaced(apps) do |app_manifest|
91
+ push_with_manifest(app_manifest, push, input)
92
+ end
93
+ end
94
+
95
+ def push_with_manifest(app_manifest, push, input)
96
+ with_filters(
97
+ :push => {
98
+ :create_app => proc { |a|
99
+ setup_env(a, app_manifest)
100
+ a
101
+ },
102
+ :push_app => proc { |a|
103
+ setup_services(a, app_manifest)
104
+ a
105
+ }
106
+ }) do
107
+ app_input = push_input_for(app_manifest, input)
108
+
109
+ push.call(app_input)
110
+ end
111
+ end
112
+
113
+ def push_input_for(app_manifest, input)
114
+ existing_app = client.app_by_name(app_manifest[:name])
115
+ rebased_input = input.rebase_given(app_manifest)
116
+
117
+ if !existing_app || input[:reset]
118
+ input = rebased_input
119
+ else
120
+ warn_reset_changes if manifest_differs?(existing_app, rebased_input)
121
+ end
122
+
123
+ input.merge(
124
+ :path => from_manifest(app_manifest[:path]),
125
+ :name => app_manifest[:name],
126
+ :bind_services => false,
127
+ :create_services => false)
128
+ end
129
+
130
+ def manifest_differs?(app, input)
131
+ apply_changes(app, input)
132
+ app.changed?
133
+ end
134
+
135
+ def create_and_save_manifest(push, input)
136
+ with_filters(
137
+ :push => { :push_app => proc { |a| ask_to_save(input, a); a } }) do
138
+ push.call
139
+ end
140
+ end
141
+ end