cf 0.1.0
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/LICENSE +30 -0
- data/Rakefile +1 -0
- data/bin/cf +14 -0
- data/lib/cf.rb +2 -0
- data/lib/cf/cli.rb +249 -0
- data/lib/cf/cli/app.rb +545 -0
- data/lib/cf/cli/command.rb +433 -0
- data/lib/cf/cli/dots.rb +130 -0
- data/lib/cf/cli/service.rb +91 -0
- data/lib/cf/cli/user.rb +71 -0
- data/lib/cf/constants.rb +10 -0
- data/lib/cf/detect.rb +60 -0
- data/lib/cf/plugin.rb +24 -0
- data/lib/cf/version.rb +3 -0
- data/plugins/manifests/main.rb +508 -0
- metadata +105 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require "cf/cli/command"
|
2
|
+
|
3
|
+
module CF
|
4
|
+
class Service < Command
|
5
|
+
desc "delete", "Delete a service"
|
6
|
+
flag(:name) { |choices|
|
7
|
+
ask "Delete which service?", :choices => choices
|
8
|
+
}
|
9
|
+
def delete(name = nil)
|
10
|
+
name ||= input(:name, client.services.collect(&:name))
|
11
|
+
|
12
|
+
with_progress("Deleting #{c(name, :blue)}") do
|
13
|
+
client.service(name).delete!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "bind", "Bind a service to an application"
|
18
|
+
flag(:name) { |choices|
|
19
|
+
ask "Which service?", :choices => choices
|
20
|
+
}
|
21
|
+
flag(:app) { |choices|
|
22
|
+
ask "Which application?", :choices => choices
|
23
|
+
}
|
24
|
+
def bind(name = nil, appname = nil)
|
25
|
+
name ||= input(:name, client.services.collect(&:name))
|
26
|
+
appname ||= input(:app, client.apps.collect(&:name))
|
27
|
+
|
28
|
+
with_progress("Binding #{c(name, :blue)} to #{c(appname, :blue)}") do
|
29
|
+
client.app(appname).bind(name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "unbind", "Unbind a service from an application"
|
34
|
+
flag(:name) { |choices|
|
35
|
+
ask "Which service?", :choices => choices
|
36
|
+
}
|
37
|
+
flag(:app) { |choices|
|
38
|
+
ask "Which application?", :choices => choices
|
39
|
+
}
|
40
|
+
def unbind(name = nil, appname = nil)
|
41
|
+
appname ||= input(:app, client.apps.collect(&:name))
|
42
|
+
|
43
|
+
app = client.app(appname)
|
44
|
+
name ||= input(:name, app.services)
|
45
|
+
|
46
|
+
with_progress("Unbinding #{c(name, :blue)} from #{c(appname, :blue)}") do
|
47
|
+
app.unbind(name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "create", "Create a service"
|
52
|
+
flag(:type) { |choices|
|
53
|
+
ask "What kind?", :choices => choices
|
54
|
+
}
|
55
|
+
flag(:name) { |vendor|
|
56
|
+
random = sprintf("%x", rand(1000000))
|
57
|
+
ask "Name?", :default => "#{vendor}-#{random}"
|
58
|
+
}
|
59
|
+
def create
|
60
|
+
choices = []
|
61
|
+
manifests = {}
|
62
|
+
client.system_services.each do |type, vendors|
|
63
|
+
vendors.each do |vendor, versions|
|
64
|
+
versions.each do |version, _|
|
65
|
+
choice = "#{vendor} #{version}"
|
66
|
+
manifests[choice] = {
|
67
|
+
:type => type,
|
68
|
+
:vendor => vendor,
|
69
|
+
:version => version
|
70
|
+
}
|
71
|
+
|
72
|
+
choices << choice
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
type = input(:type, choices)
|
78
|
+
meta = manifests[type]
|
79
|
+
|
80
|
+
service = client.service(input(:name, meta[:vendor]))
|
81
|
+
service.type = meta[:type]
|
82
|
+
service.vendor = meta[:vendor]
|
83
|
+
service.version = meta[:version]
|
84
|
+
service.tier = "free"
|
85
|
+
|
86
|
+
with_progress("Creating service") do
|
87
|
+
service.create!
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/cf/cli/user.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require "cf/cli/command"
|
2
|
+
|
3
|
+
module CF
|
4
|
+
class User < Command
|
5
|
+
desc "create [EMAIL]", "Create a user"
|
6
|
+
flag(:email) {
|
7
|
+
ask("Email")
|
8
|
+
}
|
9
|
+
flag(:password) {
|
10
|
+
ask("Password", :echo => "*", :forget => true)
|
11
|
+
}
|
12
|
+
flag(:verify) {
|
13
|
+
ask("Verify Password", :echo => "*", :forget => true)
|
14
|
+
}
|
15
|
+
def create(email = nil)
|
16
|
+
email ||= input(:email)
|
17
|
+
password = input(:password)
|
18
|
+
verify = input(:verify)
|
19
|
+
|
20
|
+
if password != verify
|
21
|
+
err "Passwords don't match."
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
with_progress("Creating user") do
|
26
|
+
client.register(email, password)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "delete [EMAIL]", "Delete a user"
|
31
|
+
flag(:really) { |email|
|
32
|
+
force? || ask("Really delete user #{c(email, :blue)}?", :default => false)
|
33
|
+
}
|
34
|
+
def delete(email)
|
35
|
+
return unless input(:really, email)
|
36
|
+
|
37
|
+
with_progress("Deleting #{c(email, :blue)}") do
|
38
|
+
client.user(email).delete!
|
39
|
+
end
|
40
|
+
ensure
|
41
|
+
forget(:really)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "passwd [EMAIL]", "Update a user's password"
|
45
|
+
flag(:email) {
|
46
|
+
ask("Email")
|
47
|
+
}
|
48
|
+
flag(:password) {
|
49
|
+
ask("Password", :echo => "*", :forget => true)
|
50
|
+
}
|
51
|
+
flag(:verify) {
|
52
|
+
ask("Verify Password", :echo => "*", :forget => true)
|
53
|
+
}
|
54
|
+
def passwd(email = nil)
|
55
|
+
email ||= input(:email)
|
56
|
+
password = input(:password)
|
57
|
+
verify = input(:verify)
|
58
|
+
|
59
|
+
if password != verify
|
60
|
+
err "Passwords don't match."
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
with_progress("Changing password") do
|
65
|
+
user = client.user(email)
|
66
|
+
user.password = password
|
67
|
+
user.update!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/cf/constants.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module CF
|
2
|
+
OLD_TARGET_FILE = "~/.vmc_target"
|
3
|
+
OLD_TOKENS_FILE = "~/.vmc_token"
|
4
|
+
|
5
|
+
CONFIG_DIR = "~/.cf"
|
6
|
+
PLUGINS_DIR = "#{CONFIG_DIR}/plugins"
|
7
|
+
TARGET_FILE = "#{CONFIG_DIR}/target"
|
8
|
+
TOKENS_FILE = "#{CONFIG_DIR}/tokens"
|
9
|
+
CRASH_FILE = "#{CONFIG_DIR}/crash"
|
10
|
+
end
|
data/lib/cf/detect.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module CF
|
2
|
+
class Detector
|
3
|
+
def initialize(client, path)
|
4
|
+
@client = client
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
def all_frameworks
|
9
|
+
info = @client.info
|
10
|
+
info["frameworks"] || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def frameworks
|
14
|
+
info = @client.info
|
15
|
+
|
16
|
+
matches = {}
|
17
|
+
all_frameworks.each do |name, meta|
|
18
|
+
matched = false
|
19
|
+
meta["detection"].first.each do |file, match|
|
20
|
+
files =
|
21
|
+
if File.file? @path
|
22
|
+
if File.fnmatch(file, @path)
|
23
|
+
[@path]
|
24
|
+
else
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
else
|
28
|
+
Dir.glob("#@path/#{file}")
|
29
|
+
end
|
30
|
+
|
31
|
+
unless files.empty?
|
32
|
+
if match == true
|
33
|
+
matched = true
|
34
|
+
elsif match == false
|
35
|
+
matched = false
|
36
|
+
break
|
37
|
+
else
|
38
|
+
files.each do |f|
|
39
|
+
contents = File.open(f, &:read)
|
40
|
+
if contents =~ Regexp.new(match)
|
41
|
+
matched = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if matched
|
49
|
+
matches[name] = meta
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if matches.size == 1
|
54
|
+
default = matches.keys.first
|
55
|
+
end
|
56
|
+
|
57
|
+
[matches, default]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/cf/plugin.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "cf/constants"
|
2
|
+
require "cf/cli"
|
3
|
+
|
4
|
+
module CF
|
5
|
+
module Plugin
|
6
|
+
@@plugins = []
|
7
|
+
|
8
|
+
def self.load_all
|
9
|
+
bundled = File.expand_path("../../../plugins/*/main.rb", __FILE__)
|
10
|
+
Dir.glob(bundled).each do |main|
|
11
|
+
require main
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir.glob(File.expand_path("#{CF::PLUGINS_DIR}/*/main.rb")).each do |main|
|
15
|
+
require main
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.Plugin(target = CLI, &blk)
|
21
|
+
# SUPER FANCY PLUGIN SYSTEM
|
22
|
+
target.class_eval &blk
|
23
|
+
end
|
24
|
+
end
|
data/lib/cf/version.rb
ADDED
@@ -0,0 +1,508 @@
|
|
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
|