cf 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cf/cli.rb +16 -11
- data/lib/cf/cli/app.rb +31 -8
- data/lib/cf/cli/command.rb +9 -5
- data/lib/cf/cli/dots.rb +5 -2
- data/lib/cf/constants.rb +2 -2
- data/lib/cf/detect.rb +4 -0
- data/lib/cf/plugin.rb +21 -5
- data/lib/cf/version.rb +1 -1
- metadata +86 -49
- data/plugins/manifests/main.rb +0 -508
data/lib/cf/cli.rb
CHANGED
@@ -5,6 +5,8 @@ require "cf/cli/user"
|
|
5
5
|
|
6
6
|
module CF
|
7
7
|
class CLI < App # subclass App since we operate on Apps by default
|
8
|
+
class_option :proxy, :aliases => "-u", :desc => "Proxy user"
|
9
|
+
|
8
10
|
class_option :verbose,
|
9
11
|
:type => :boolean, :aliases => "-v", :desc => "Verbose"
|
10
12
|
|
@@ -95,17 +97,20 @@ module CF
|
|
95
97
|
puts "target: #{b(client.target)}"
|
96
98
|
puts " version: #{info["version"]}"
|
97
99
|
puts " support: #{info["support"]}"
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
100
|
+
|
101
|
+
if info["user"]
|
102
|
+
puts ""
|
103
|
+
puts "user: #{b(info["user"])}"
|
104
|
+
puts " usage:"
|
105
|
+
|
106
|
+
limits = info["limits"]
|
107
|
+
info["usage"].each do |k, v|
|
108
|
+
m = limits[k]
|
109
|
+
if k == "memory"
|
110
|
+
puts " #{k}: #{usage(v * 1024 * 1024, m * 1024 * 1024)}"
|
111
|
+
else
|
112
|
+
puts " #{k}: #{b(v)} of #{b(m)} limit"
|
113
|
+
end
|
109
114
|
end
|
110
115
|
end
|
111
116
|
end
|
data/lib/cf/cli/app.rb
CHANGED
@@ -23,6 +23,25 @@ module CF
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
desc "health ...APPS", "Get application health"
|
27
|
+
def health(*names)
|
28
|
+
apps =
|
29
|
+
with_progress("Getting application health") do
|
30
|
+
names.collect do |n|
|
31
|
+
[n, app_status(client.app(n))]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
apps.each do |name, status|
|
36
|
+
unless simple_output?
|
37
|
+
puts ""
|
38
|
+
print "#{c(name, :blue)}: "
|
39
|
+
end
|
40
|
+
|
41
|
+
puts status
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
26
45
|
desc "stop [APP]", "Stop an application"
|
27
46
|
def stop(name)
|
28
47
|
with_progress("Stopping #{c(name, :blue)}") do |s|
|
@@ -274,7 +293,7 @@ module CF
|
|
274
293
|
err "The 'update' command is no longer used; use 'push' instead."
|
275
294
|
end
|
276
295
|
|
277
|
-
desc "stats", "Display application instance status"
|
296
|
+
desc "stats [APP]", "Display application instance status"
|
278
297
|
def stats(name)
|
279
298
|
stats =
|
280
299
|
with_progress("Getting stats") do
|
@@ -500,6 +519,16 @@ module CF
|
|
500
519
|
end
|
501
520
|
end
|
502
521
|
|
522
|
+
def app_status(a)
|
523
|
+
health = a.health
|
524
|
+
|
525
|
+
if a.debug_mode == "suspend" && health == "0%"
|
526
|
+
c("suspended", :yellow)
|
527
|
+
else
|
528
|
+
c(health.downcase, state_color(health))
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
503
532
|
def display_app(a)
|
504
533
|
if simple_output?
|
505
534
|
puts a.name
|
@@ -508,13 +537,7 @@ module CF
|
|
508
537
|
|
509
538
|
puts ""
|
510
539
|
|
511
|
-
|
512
|
-
|
513
|
-
if a.debug_mode == "suspend" && health == "0%"
|
514
|
-
status = c("suspended", :yellow)
|
515
|
-
else
|
516
|
-
status = c(health.downcase, state_color(health))
|
517
|
-
end
|
540
|
+
status = app_status(a)
|
518
541
|
|
519
542
|
print "#{c(a.name, :blue)}: #{status}"
|
520
543
|
|
data/lib/cf/cli/command.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "thor"
|
2
2
|
require "interact"
|
3
|
-
require "
|
3
|
+
require "yaml"
|
4
4
|
|
5
5
|
require "cfoundry"
|
6
6
|
|
@@ -357,7 +357,7 @@ module CF
|
|
357
357
|
end
|
358
358
|
|
359
359
|
def tokens
|
360
|
-
|
360
|
+
YAML.load_file(tokens_file)
|
361
361
|
end
|
362
362
|
|
363
363
|
def client_token
|
@@ -367,8 +367,8 @@ module CF
|
|
367
367
|
def save_tokens(ts)
|
368
368
|
ensure_config_dir
|
369
369
|
|
370
|
-
File.open(File.expand_path(CF::TOKENS_FILE), "w") do |
|
371
|
-
|
370
|
+
File.open(File.expand_path(CF::TOKENS_FILE), "w") do |io|
|
371
|
+
YAML.dump(ts, io)
|
372
372
|
end
|
373
373
|
end
|
374
374
|
|
@@ -385,7 +385,11 @@ module CF
|
|
385
385
|
end
|
386
386
|
|
387
387
|
def client
|
388
|
-
@client
|
388
|
+
return @client if @client
|
389
|
+
|
390
|
+
@client = CFoundry::Client.new(client_target, client_token)
|
391
|
+
@client.proxy = options[:proxy]
|
392
|
+
@client
|
389
393
|
end
|
390
394
|
|
391
395
|
def usage(used, limit)
|
data/lib/cf/cli/dots.rb
CHANGED
@@ -69,9 +69,12 @@ module CF
|
|
69
69
|
}
|
70
70
|
|
71
71
|
# colored text
|
72
|
-
|
72
|
+
#
|
73
|
+
# shouldn't use bright colors, as some color themes abuse
|
74
|
+
# the bright palette (I'm looking at you, Solarized)
|
75
|
+
def c(str, color)
|
73
76
|
return str unless color?
|
74
|
-
"\e[
|
77
|
+
"\e[3#{COLOR_CODES[color]}m#{str}\e[0m"
|
75
78
|
end
|
76
79
|
module_function :c
|
77
80
|
|
data/lib/cf/constants.rb
CHANGED
@@ -3,8 +3,8 @@ module CF
|
|
3
3
|
OLD_TOKENS_FILE = "~/.vmc_token"
|
4
4
|
|
5
5
|
CONFIG_DIR = "~/.cf"
|
6
|
-
|
6
|
+
PLUGINS_FILE = "#{CONFIG_DIR}/plugins.yml"
|
7
7
|
TARGET_FILE = "#{CONFIG_DIR}/target"
|
8
|
-
TOKENS_FILE = "#{CONFIG_DIR}/tokens"
|
8
|
+
TOKENS_FILE = "#{CONFIG_DIR}/tokens.yml"
|
9
9
|
CRASH_FILE = "#{CONFIG_DIR}/crash"
|
10
10
|
end
|
data/lib/cf/detect.rb
CHANGED
data/lib/cf/plugin.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require "set"
|
2
|
+
require "yaml"
|
3
|
+
|
1
4
|
require "cf/constants"
|
2
5
|
require "cf/cli"
|
3
6
|
|
@@ -6,13 +9,26 @@ module CF
|
|
6
9
|
@@plugins = []
|
7
10
|
|
8
11
|
def self.load_all
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
# auto-load gems with 'vmc-plugin' in their name
|
13
|
+
enabled =
|
14
|
+
Set.new(
|
15
|
+
Gem::Specification.find_all { |s|
|
16
|
+
s.name =~ /vmc-plugin/
|
17
|
+
}.collect(&:name))
|
18
|
+
|
19
|
+
# allow explicit enabling/disabling of gems via config
|
20
|
+
plugins = File.expand_path(CF::PLUGINS_FILE)
|
21
|
+
if File.exists?(plugins) && yaml = YAML.load_file(plugins)
|
22
|
+
enabled += yaml["enabled"] if yaml["enabled"]
|
23
|
+
enabled -= yaml["disabled"] if yaml["disabled"]
|
12
24
|
end
|
13
25
|
|
14
|
-
|
15
|
-
|
26
|
+
# load up each gem's 'plugin' file
|
27
|
+
#
|
28
|
+
# we require this file specifically so people can require the gem
|
29
|
+
# without it plugging into CF
|
30
|
+
enabled.each do |gemname|
|
31
|
+
require "#{gemname}/plugin"
|
16
32
|
end
|
17
33
|
end
|
18
34
|
end
|
data/lib/cf/version.rb
CHANGED
metadata
CHANGED
@@ -1,68 +1,96 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cf
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Alex Suraci
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2012-05-01 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: interact
|
16
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
24
|
none: false
|
18
|
-
requirements:
|
25
|
+
requirements:
|
19
26
|
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 13
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 4
|
32
|
+
- 1
|
21
33
|
version: 0.4.1
|
22
34
|
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: cfoundry
|
23
38
|
prerelease: false
|
24
|
-
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: json_pure
|
27
|
-
requirement: &70204736022880 !ruby/object:Gem::Requirement
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
28
40
|
none: false
|
29
|
-
requirements:
|
41
|
+
requirements:
|
30
42
|
- - ~>
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 27
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
version: 0.1.0
|
33
50
|
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: thor
|
34
54
|
prerelease: false
|
35
|
-
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: cfoundry
|
38
|
-
requirement: &70204736022420 !ruby/object:Gem::Requirement
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
56
|
none: false
|
40
|
-
requirements:
|
57
|
+
requirements:
|
41
58
|
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 43
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 14
|
64
|
+
- 6
|
65
|
+
version: 0.14.6
|
44
66
|
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: manifests-vmc-plugin
|
45
70
|
prerelease: false
|
46
|
-
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: thor
|
49
|
-
requirement: &70204736021940 !ruby/object:Gem::Requirement
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
50
72
|
none: false
|
51
|
-
requirements:
|
73
|
+
requirements:
|
52
74
|
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 27
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
- 1
|
80
|
+
- 0
|
81
|
+
version: 0.1.0
|
55
82
|
type: :runtime
|
56
|
-
|
57
|
-
version_requirements: *70204736021940
|
83
|
+
version_requirements: *id004
|
58
84
|
description:
|
59
|
-
email:
|
85
|
+
email:
|
60
86
|
- asuraci@vmware.com
|
61
|
-
executables:
|
87
|
+
executables:
|
62
88
|
- cf
|
63
89
|
extensions: []
|
90
|
+
|
64
91
|
extra_rdoc_files: []
|
65
|
-
|
92
|
+
|
93
|
+
files:
|
66
94
|
- LICENSE
|
67
95
|
- Rakefile
|
68
96
|
- lib/cf/cli/app.rb
|
@@ -76,30 +104,39 @@ files:
|
|
76
104
|
- lib/cf/plugin.rb
|
77
105
|
- lib/cf/version.rb
|
78
106
|
- lib/cf.rb
|
79
|
-
- plugins/manifests/main.rb
|
80
107
|
- bin/cf
|
81
108
|
homepage: http://cloudfoundry.com/
|
82
109
|
licenses: []
|
110
|
+
|
83
111
|
post_install_message:
|
84
112
|
rdoc_options: []
|
85
|
-
|
113
|
+
|
114
|
+
require_paths:
|
86
115
|
- lib
|
87
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
117
|
none: false
|
89
|
-
requirements:
|
90
|
-
- -
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
|
93
|
-
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
126
|
none: false
|
95
|
-
requirements:
|
96
|
-
- -
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
99
134
|
requirements: []
|
135
|
+
|
100
136
|
rubyforge_project: cf
|
101
|
-
rubygems_version: 1.8.
|
137
|
+
rubygems_version: 1.8.23
|
102
138
|
signing_key:
|
103
139
|
specification_version: 3
|
104
140
|
summary: Friendly command-line interface for Cloud Foundry.
|
105
141
|
test_files: []
|
142
|
+
|
data/plugins/manifests/main.rb
DELETED
@@ -1,508 +0,0 @@
|
|
1
|
-
require "yaml"
|
2
|
-
require "set"
|
3
|
-
require "cf/plugin"
|
4
|
-
|
5
|
-
CF.Plugin do
|
6
|
-
class_option :manifest,
|
7
|
-
:aliases => "-m", :desc => "Manifest file"
|
8
|
-
|
9
|
-
class_option :path,
|
10
|
-
:aliases => "-p", :desc => "Application path"
|
11
|
-
end
|
12
|
-
|
13
|
-
module CF::Plugins
|
14
|
-
module Manifests
|
15
|
-
MANIFEST_FILE = "manifest.yml"
|
16
|
-
|
17
|
-
def manifest
|
18
|
-
return @manifest if @manifest
|
19
|
-
|
20
|
-
if manifest_file && File.exists?(manifest_file)
|
21
|
-
@manifest = load_manifest(manifest_file)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def save_manifest(save_to = manifest_file)
|
26
|
-
err "No manifest to save!" unless @manifest
|
27
|
-
|
28
|
-
File.open(save_to, "w") do |io|
|
29
|
-
YAML.dump(@manifest, io)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# find the manifest file to work with
|
34
|
-
def manifest_file
|
35
|
-
return options[:manifest] if options[:manifest]
|
36
|
-
return @manifest_file if @manifest_file
|
37
|
-
|
38
|
-
where = Dir.pwd
|
39
|
-
while true
|
40
|
-
if File.exists?(File.join(where, MANIFEST_FILE))
|
41
|
-
@manifest_file = File.join(where, MANIFEST_FILE)
|
42
|
-
break
|
43
|
-
elsif File.basename(where) == "/"
|
44
|
-
@manifest_file = nil
|
45
|
-
break
|
46
|
-
else
|
47
|
-
where = File.expand_path("../", where)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
@manifest_file
|
52
|
-
end
|
53
|
-
|
54
|
-
# load and resolve a given manifest file
|
55
|
-
def load_manifest(file)
|
56
|
-
manifest = build_manifest(file)
|
57
|
-
resolve_manifest(manifest)
|
58
|
-
manifest
|
59
|
-
end
|
60
|
-
|
61
|
-
# parse a manifest and merge with its inherited manifests
|
62
|
-
def build_manifest(file)
|
63
|
-
manifest = YAML.load_file file
|
64
|
-
|
65
|
-
Array(manifest["inherit"]).each do |p|
|
66
|
-
manifest = merge_parent(manifest, p)
|
67
|
-
end
|
68
|
-
|
69
|
-
manifest
|
70
|
-
end
|
71
|
-
|
72
|
-
# merge the manifest at `path' into the `child'
|
73
|
-
def merge_parent(child, path)
|
74
|
-
file = File.expand_path("../" + path, manifest_file)
|
75
|
-
merge_manifest(child, build_manifest(file))
|
76
|
-
end
|
77
|
-
|
78
|
-
# deep hash merge
|
79
|
-
def merge_manifest(child, parent)
|
80
|
-
merge = proc do |_, old, new|
|
81
|
-
if new.is_a?(Hash) and old.is_a?(Hash)
|
82
|
-
old.merge(new, &merge)
|
83
|
-
else
|
84
|
-
new
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
parent.merge(child, &merge)
|
89
|
-
end
|
90
|
-
|
91
|
-
# resolve symbols in a manifest
|
92
|
-
def resolve_manifest(manifest)
|
93
|
-
if apps = manifest["applications"]
|
94
|
-
apps.each_value do |v|
|
95
|
-
resolve_lexically(v, [manifest])
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
resolve_lexically(manifest, [manifest])
|
100
|
-
|
101
|
-
nil
|
102
|
-
end
|
103
|
-
|
104
|
-
# resolve symbols, with hashes introducing new lexical symbols
|
105
|
-
def resolve_lexically(val, ctx)
|
106
|
-
case val
|
107
|
-
when Hash
|
108
|
-
val.each_value do |v|
|
109
|
-
resolve_lexically(v, [val] + ctx)
|
110
|
-
end
|
111
|
-
when Array
|
112
|
-
val.each do |v|
|
113
|
-
resolve_lexically(v, ctx)
|
114
|
-
end
|
115
|
-
when String
|
116
|
-
val.gsub!(/\$\{([[:alnum:]\-]+)\}/) do
|
117
|
-
resolve_symbol($1, ctx)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
nil
|
122
|
-
end
|
123
|
-
|
124
|
-
# resolve a symbol to its value, and then resolve that value
|
125
|
-
def resolve_symbol(sym, ctx)
|
126
|
-
case sym
|
127
|
-
when "target-url"
|
128
|
-
target_url(ctx)
|
129
|
-
|
130
|
-
when "target-base"
|
131
|
-
target_url(ctx).sub(/^[^\.]+\./, "")
|
132
|
-
|
133
|
-
when "random-word"
|
134
|
-
"%04x" % [rand(0x0100000)]
|
135
|
-
|
136
|
-
else
|
137
|
-
found = find_symbol(sym, ctx)
|
138
|
-
|
139
|
-
if found
|
140
|
-
resolve_lexically(found, ctx)
|
141
|
-
found
|
142
|
-
else
|
143
|
-
err("Unknown symbol in manifest: #{sym}")
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
# get the target url from either the manifest or the current client
|
149
|
-
def target_url(ctx = [])
|
150
|
-
find_symbol("target", ctx) || client_target
|
151
|
-
end
|
152
|
-
|
153
|
-
# search for a symbol introduced in the lexical context
|
154
|
-
def find_symbol(sym, ctx)
|
155
|
-
ctx.each do |h|
|
156
|
-
if val = resolve_in(h, sym)
|
157
|
-
return val
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
nil
|
162
|
-
end
|
163
|
-
|
164
|
-
# find a value, searching in explicit properties first
|
165
|
-
def resolve_in(hash, *where)
|
166
|
-
find_in_hash(hash, ["properties"] + where) ||
|
167
|
-
find_in_hash(hash, where)
|
168
|
-
end
|
169
|
-
|
170
|
-
# helper for following a path of values in a hash
|
171
|
-
def find_in_hash(hash, where)
|
172
|
-
what = hash
|
173
|
-
where.each do |x|
|
174
|
-
return nil unless what.is_a?(Hash)
|
175
|
-
what = what[x]
|
176
|
-
end
|
177
|
-
|
178
|
-
what
|
179
|
-
end
|
180
|
-
|
181
|
-
MANIFEST_META = ["applications", "properties"]
|
182
|
-
|
183
|
-
def toplevel_attributes
|
184
|
-
if m = manifest
|
185
|
-
m.reject do |k, _|
|
186
|
-
MANIFEST_META.include? k
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def app_info(find_path)
|
192
|
-
return unless manifest and manifest["applications"]
|
193
|
-
|
194
|
-
manifest["applications"].each do |path, info|
|
195
|
-
app = File.expand_path("../" + path, manifest_file)
|
196
|
-
if find_path == app
|
197
|
-
return toplevel_attributes.merge info
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
nil
|
202
|
-
end
|
203
|
-
|
204
|
-
# call a block for each app in a manifest (in dependency order), setting
|
205
|
-
# inputs for each app
|
206
|
-
def each_app
|
207
|
-
given_path = passed_value(:path)
|
208
|
-
|
209
|
-
if manifest and all_apps = manifest["applications"]
|
210
|
-
# given a specific application
|
211
|
-
if given_path
|
212
|
-
full_path = File.expand_path(given_path)
|
213
|
-
|
214
|
-
if info = app_info(full_path)
|
215
|
-
with_app(full_path, info) do
|
216
|
-
yield info
|
217
|
-
end
|
218
|
-
else
|
219
|
-
raise "Path #{given_path} is not described by the manifest."
|
220
|
-
end
|
221
|
-
else
|
222
|
-
# all apps in the manifest
|
223
|
-
ordered_by_deps(all_apps).each do |path|
|
224
|
-
app = File.expand_path("../" + path, manifest_file)
|
225
|
-
info = app_info(app)
|
226
|
-
|
227
|
-
with_app(app, info) do
|
228
|
-
yield info
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
true
|
234
|
-
|
235
|
-
# manually created or legacy single-app manifest
|
236
|
-
elsif single = toplevel_attributes
|
237
|
-
with_app(full_path || ".", single) do
|
238
|
-
yield single
|
239
|
-
end
|
240
|
-
|
241
|
-
true
|
242
|
-
|
243
|
-
else
|
244
|
-
false
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
private
|
249
|
-
|
250
|
-
def inputs
|
251
|
-
@inputs ||= {}
|
252
|
-
end
|
253
|
-
|
254
|
-
# call the block as if the app info and path were given as flags
|
255
|
-
def with_app(path, info)
|
256
|
-
before_path = inputs[:path]
|
257
|
-
before_info = {}
|
258
|
-
|
259
|
-
inputs[:path] = path
|
260
|
-
|
261
|
-
info.each do |k, v|
|
262
|
-
before_info[k.to_sym] = inputs[k.to_sym]
|
263
|
-
inputs[k.to_sym] = v
|
264
|
-
end
|
265
|
-
|
266
|
-
yield
|
267
|
-
ensure
|
268
|
-
if before_path.nil?
|
269
|
-
inputs.delete :path
|
270
|
-
else
|
271
|
-
inputs[:path] = before_path
|
272
|
-
end
|
273
|
-
|
274
|
-
before_info.each do |k, v|
|
275
|
-
if v.nil?
|
276
|
-
inputs.delete k
|
277
|
-
else
|
278
|
-
inputs[k] = v
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
# sort applications in dependency order
|
284
|
-
# e.g. if A depends on B, B will be listed before A
|
285
|
-
def ordered_by_deps(apps, abspaths = nil, processed = Set[])
|
286
|
-
unless abspaths
|
287
|
-
abspaths = {}
|
288
|
-
apps.each do |p, i|
|
289
|
-
ep = File.expand_path("../" + p, manifest_file)
|
290
|
-
abspaths[ep] = i
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
ordered = []
|
295
|
-
apps.each do |path, info|
|
296
|
-
epath = File.expand_path("../" + path, manifest_file)
|
297
|
-
|
298
|
-
if deps = info["depends-on"]
|
299
|
-
dep_apps = {}
|
300
|
-
deps.each do |dep|
|
301
|
-
edep = File.expand_path("../" + dep, manifest_file)
|
302
|
-
|
303
|
-
err "Circular dependency detected." if processed.include? edep
|
304
|
-
|
305
|
-
dep_apps[dep] = abspaths[edep]
|
306
|
-
end
|
307
|
-
|
308
|
-
processed.add(epath)
|
309
|
-
|
310
|
-
ordered += ordered_by_deps(dep_apps, abspaths, processed)
|
311
|
-
ordered << path
|
312
|
-
elsif not processed.include? epath
|
313
|
-
ordered << path
|
314
|
-
processed.add(epath)
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
ordered
|
319
|
-
end
|
320
|
-
|
321
|
-
# detect changes in app info, and update the app if necessary.
|
322
|
-
#
|
323
|
-
# redeploys the app if necessary (after prompting the user), e.g. for
|
324
|
-
# runtime/framework change
|
325
|
-
def sync_changes(info)
|
326
|
-
app = client.app(info["name"])
|
327
|
-
return unless app.exists?
|
328
|
-
|
329
|
-
diff = {}
|
330
|
-
need_restage = []
|
331
|
-
info.each do |k, v|
|
332
|
-
case k
|
333
|
-
when /ur[li]s?/
|
334
|
-
old = app.urls
|
335
|
-
if old != Array(v)
|
336
|
-
diff[k] = [old, v]
|
337
|
-
app.urls = Array(v)
|
338
|
-
end
|
339
|
-
when "env"
|
340
|
-
old = app.env
|
341
|
-
if old != v
|
342
|
-
diff[k] = [old, v]
|
343
|
-
app.env = v
|
344
|
-
end
|
345
|
-
when "framework", "runtime"
|
346
|
-
old = app.send(k)
|
347
|
-
if old != v
|
348
|
-
diff[k] = [old, v]
|
349
|
-
app.send(:"#{k}=", v)
|
350
|
-
need_restage << k
|
351
|
-
end
|
352
|
-
when "instances"
|
353
|
-
old = app.total_instances
|
354
|
-
if old != v
|
355
|
-
diff[k] = [old, v]
|
356
|
-
app.total_instances = v
|
357
|
-
end
|
358
|
-
when "mem", "memory"
|
359
|
-
old = app.memory
|
360
|
-
new = megabytes(v)
|
361
|
-
if old != new
|
362
|
-
diff[k] = [old, new]
|
363
|
-
app.memory = new
|
364
|
-
end
|
365
|
-
end
|
366
|
-
end
|
367
|
-
|
368
|
-
return if diff.empty?
|
369
|
-
|
370
|
-
unless simple_output?
|
371
|
-
puts "Detected the following changes to #{c(app.name, :blue)}:"
|
372
|
-
diff.each do |k, d|
|
373
|
-
old, new = d
|
374
|
-
label = c(k, need_restage.include?(k) ? :red : :green)
|
375
|
-
puts " #{label}: #{old.inspect} #{c("->", :black)} #{new.inspect}"
|
376
|
-
end
|
377
|
-
|
378
|
-
puts ""
|
379
|
-
end
|
380
|
-
|
381
|
-
if need_restage.empty?
|
382
|
-
with_progress("Updating #{c(app.name, :blue)}") do
|
383
|
-
app.update!
|
384
|
-
end
|
385
|
-
else
|
386
|
-
unless simple_output?
|
387
|
-
puts "The following changes require the app to be recreated:"
|
388
|
-
need_restage.each do |n|
|
389
|
-
puts " #{c(n, :magenta)}"
|
390
|
-
end
|
391
|
-
puts ""
|
392
|
-
end
|
393
|
-
|
394
|
-
if force? || ask("Redeploy?", :default => false)
|
395
|
-
with_progress("Deleting #{c(app.name, :blue)}") do
|
396
|
-
app.delete!
|
397
|
-
end
|
398
|
-
|
399
|
-
with_progress("Recreating #{c(app.name, :blue)}") do
|
400
|
-
app.create!
|
401
|
-
end
|
402
|
-
end
|
403
|
-
end
|
404
|
-
end
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
CF.Plugin(CF::App) do
|
409
|
-
include CF::Plugins::Manifests
|
410
|
-
|
411
|
-
# basic commands that, when given no args, act on the
|
412
|
-
# app(s) described by the manifest, in dependency-order
|
413
|
-
[:start, :instances, :logs].each do |wrap|
|
414
|
-
around(wrap) do |cmd, args|
|
415
|
-
if args.empty?
|
416
|
-
each_app do |a|
|
417
|
-
cmd.call(a["name"])
|
418
|
-
puts "" unless simple_output?
|
419
|
-
end || err("No applications to act on.")
|
420
|
-
else
|
421
|
-
cmd.call(args)
|
422
|
-
end
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
# same as above but in reverse dependency-order
|
427
|
-
[:stop, :delete].each do |wrap|
|
428
|
-
around(wrap) do |cmd, args|
|
429
|
-
if args.empty?
|
430
|
-
reversed = []
|
431
|
-
each_app do |a|
|
432
|
-
reversed.unshift a["name"]
|
433
|
-
end || err("No applications to act on.")
|
434
|
-
|
435
|
-
reversed.each do |name|
|
436
|
-
cmd.call(name)
|
437
|
-
puts "" unless simple_output?
|
438
|
-
end
|
439
|
-
else
|
440
|
-
cmd.call(args)
|
441
|
-
end
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
# stop apps in reverse dependency order,
|
446
|
-
# and then start in dependency order
|
447
|
-
around(:restart) do |cmd, args|
|
448
|
-
if args.empty?
|
449
|
-
reversed = []
|
450
|
-
forwards = []
|
451
|
-
each_app do |a|
|
452
|
-
reversed.unshift a["name"]
|
453
|
-
forwards << a["name"]
|
454
|
-
end || err("No applications to act on.")
|
455
|
-
|
456
|
-
reversed.each do |name|
|
457
|
-
stop(name)
|
458
|
-
end
|
459
|
-
|
460
|
-
puts "" unless simple_output?
|
461
|
-
|
462
|
-
forwards.each do |name|
|
463
|
-
start(name)
|
464
|
-
end
|
465
|
-
else
|
466
|
-
cmd.call(args)
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
# push and sync meta changes in the manifest
|
471
|
-
# also sets env data on creation if present in manifest
|
472
|
-
around(:push) do |push, args|
|
473
|
-
if args.empty?
|
474
|
-
all_pushed =
|
475
|
-
each_app do |a|
|
476
|
-
app = client.app(a["name"])
|
477
|
-
updating = app.exists?
|
478
|
-
|
479
|
-
start = input(:start)
|
480
|
-
|
481
|
-
begin
|
482
|
-
inputs[:start] = false
|
483
|
-
|
484
|
-
sync_changes(a)
|
485
|
-
push.call(a["name"])
|
486
|
-
|
487
|
-
unless updating
|
488
|
-
app.env = a["env"]
|
489
|
-
|
490
|
-
if start
|
491
|
-
start(a["name"])
|
492
|
-
else
|
493
|
-
app.update!
|
494
|
-
end
|
495
|
-
end
|
496
|
-
ensure
|
497
|
-
inputs[:start] = start
|
498
|
-
end
|
499
|
-
|
500
|
-
puts "" unless simple_output?
|
501
|
-
end
|
502
|
-
|
503
|
-
push.call unless all_pushed
|
504
|
-
else
|
505
|
-
push.call(args)
|
506
|
-
end
|
507
|
-
end
|
508
|
-
end
|