cf 0.1.2 → 0.1.3
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.
- 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
|