cf 0.6.1.rc9 → 0.6.1.rc10
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.rb +4 -0
- data/lib/cf/cli.rb +8 -3
- data/lib/cf/version.rb +1 -1
- data/lib/console/README.md +16 -0
- data/lib/console/console.rb +187 -0
- data/lib/console/plugin.rb +30 -0
- data/lib/manifests/README.md +13 -0
- data/lib/manifests/errors.rb +35 -0
- data/lib/manifests/loader.rb +31 -0
- data/lib/manifests/loader/builder.rb +39 -0
- data/lib/manifests/loader/normalizer.rb +145 -0
- data/lib/manifests/loader/resolver.rb +79 -0
- data/lib/manifests/manifests.rb +304 -0
- data/lib/manifests/plugin.rb +141 -0
- data/lib/tunnel/README.md +29 -0
- data/lib/tunnel/helper-app/Gemfile +11 -0
- data/lib/tunnel/helper-app/Gemfile.lock +48 -0
- data/lib/tunnel/helper-app/server.rb +43 -0
- data/lib/tunnel/plugin.rb +183 -0
- data/lib/tunnel/tunnel.rb +305 -0
- data/spec/assets/rails328_ruby187_app/Gemfile +39 -0
- data/spec/assets/rails328_ruby187_app/README.rdoc +261 -0
- data/spec/assets/rails328_ruby187_app/Rakefile +7 -0
- data/spec/assets/rails328_ruby187_app/app/assets/images/rails.png +0 -0
- data/spec/assets/rails328_ruby187_app/app/assets/javascripts/application.js +15 -0
- data/spec/assets/rails328_ruby187_app/app/assets/stylesheets/application.css +13 -0
- data/spec/assets/rails328_ruby187_app/app/controllers/application_controller.rb +3 -0
- data/spec/assets/rails328_ruby187_app/app/helpers/application_helper.rb +2 -0
- data/spec/assets/rails328_ruby187_app/app/views/layouts/application.html.erb +14 -0
- data/spec/assets/rails328_ruby187_app/config.ru +4 -0
- data/spec/assets/rails328_ruby187_app/config/application.rb +62 -0
- data/spec/assets/rails328_ruby187_app/config/boot.rb +6 -0
- data/spec/assets/rails328_ruby187_app/config/database.yml +25 -0
- data/spec/assets/rails328_ruby187_app/config/environment.rb +5 -0
- data/spec/assets/rails328_ruby187_app/config/environments/development.rb +37 -0
- data/spec/assets/rails328_ruby187_app/config/environments/production.rb +67 -0
- data/spec/assets/rails328_ruby187_app/config/environments/test.rb +37 -0
- data/spec/assets/rails328_ruby187_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/assets/rails328_ruby187_app/config/initializers/inflections.rb +15 -0
- data/spec/assets/rails328_ruby187_app/config/initializers/mime_types.rb +5 -0
- data/spec/assets/rails328_ruby187_app/config/initializers/secret_token.rb +7 -0
- data/spec/assets/rails328_ruby187_app/config/initializers/session_store.rb +8 -0
- data/spec/assets/rails328_ruby187_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/assets/rails328_ruby187_app/config/locales/en.yml +5 -0
- data/spec/assets/rails328_ruby187_app/config/routes.rb +58 -0
- data/spec/assets/rails328_ruby187_app/db/seeds.rb +7 -0
- data/spec/assets/rails328_ruby187_app/doc/README_FOR_APP +2 -0
- data/spec/assets/rails328_ruby187_app/manifest.yml +7 -0
- data/spec/assets/rails328_ruby187_app/public/404.html +26 -0
- data/spec/assets/rails328_ruby187_app/public/422.html +26 -0
- data/spec/assets/rails328_ruby187_app/public/500.html +25 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css.gz +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js +17 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js.gz +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application.css +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application.css.gz +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application.js +17 -0
- data/spec/assets/rails328_ruby187_app/public/assets/application.js.gz +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/manifest.yml +4 -0
- data/spec/assets/rails328_ruby187_app/public/assets/rails-be8732dac73d845ac5b142c8fb5f9fb0.png +0 -0
- data/spec/assets/rails328_ruby187_app/public/assets/rails.png +0 -0
- data/spec/assets/rails328_ruby187_app/public/favicon.ico +0 -0
- data/spec/assets/rails328_ruby187_app/public/index.html +241 -0
- data/spec/assets/rails328_ruby187_app/public/robots.txt +5 -0
- data/spec/assets/rails328_ruby187_app/script/rails +6 -0
- data/spec/assets/rails328_ruby187_app/test/performance/browsing_test.rb +12 -0
- data/spec/assets/rails328_ruby187_app/test/test_helper.rb +13 -0
- data/spec/cf/cli/app/stats_spec.rb +20 -20
- data/spec/cf/cli_spec.rb +37 -18
- data/spec/console/console_spec.rb +189 -0
- data/spec/features/push_flow_spec.rb +1 -1
- data/spec/manifests/errors_spec.rb +43 -0
- data/spec/manifests/loader/builder_spec.rb +80 -0
- data/spec/manifests/loader/normalizer_spec.rb +158 -0
- data/spec/manifests/manifests_spec.rb +309 -0
- data/spec/manifests/plugin_spec.rb +362 -0
- data/spec/tunnel/plugin_spec.rb +31 -0
- metadata +184 -25
data/lib/cf.rb
CHANGED
|
@@ -6,3 +6,7 @@ command_files = "../cf/cli/{app,route,domain,organization,space,service,start,us
|
|
|
6
6
|
Dir[File.expand_path(command_files, __FILE__)].each do |file|
|
|
7
7
|
require file unless File.basename(file) == 'base.rb'
|
|
8
8
|
end
|
|
9
|
+
|
|
10
|
+
require "tunnel/plugin"
|
|
11
|
+
require "console/plugin"
|
|
12
|
+
require "manifests/plugin"
|
data/lib/cf/cli.rb
CHANGED
|
@@ -26,8 +26,11 @@ module CF
|
|
|
26
26
|
option :help, :desc => "Show command usage", :alias => "-h",
|
|
27
27
|
:default => false
|
|
28
28
|
|
|
29
|
-
option :
|
|
30
|
-
:value => :
|
|
29
|
+
option :http_proxy, :desc => "Connect though an http proxy server", :alias => "--http-proxy",
|
|
30
|
+
:value => :http_proxy
|
|
31
|
+
|
|
32
|
+
option :https_proxy, :desc => "Connect though an https proxy server", :alias => "--https-proxy",
|
|
33
|
+
:value => :https_proxy
|
|
31
34
|
|
|
32
35
|
option :version, :desc => "Print version number", :alias => "-v",
|
|
33
36
|
:default => false
|
|
@@ -380,9 +383,11 @@ module CF
|
|
|
380
383
|
token = info[:token] && CFoundry::AuthToken.from_hash(info)
|
|
381
384
|
|
|
382
385
|
fail "V1 targets are no longer supported." if info[:version] == 1
|
|
383
|
-
fail "User switching not implemented." if input[:proxy]
|
|
384
386
|
|
|
385
387
|
@@client = CFoundry::V2::Client.new(target, token)
|
|
388
|
+
|
|
389
|
+
@@client.http_proxy = input[:http_proxy] || ENV['HTTP_PROXY'] || ENV['http_proxy']
|
|
390
|
+
@@client.https_proxy = input[:https_proxy] || ENV['HTTPS_PROXY'] || ENV['https_proxy']
|
|
386
391
|
@@client.trace = input[:trace]
|
|
387
392
|
|
|
388
393
|
uri = URI.parse(target)
|
data/lib/cf/version.rb
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[](https://travis-ci.org/cloudfoundry/console-cf-plugin)
|
|
2
|
+
[](http://badge.fury.io/rb/console-cf-plugin)
|
|
3
|
+
|
|
4
|
+
## Console
|
|
5
|
+
### Info
|
|
6
|
+
This plugin lets you connect to a Cloud Foundry application via telnet.
|
|
7
|
+
|
|
8
|
+
### Installation
|
|
9
|
+
```
|
|
10
|
+
gem install console-cf-plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
```
|
|
15
|
+
console APP Open a console connected to your app
|
|
16
|
+
```
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
require "net/telnet"
|
|
2
|
+
require "readline"
|
|
3
|
+
|
|
4
|
+
require "tunnel/tunnel"
|
|
5
|
+
|
|
6
|
+
class CFConsole < CFTunnel
|
|
7
|
+
def initialize(client, app, port = 10000)
|
|
8
|
+
@client = client
|
|
9
|
+
@app = app
|
|
10
|
+
@port = port
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_connection_info(auth)
|
|
14
|
+
instances = @app.instances
|
|
15
|
+
if instances.empty?
|
|
16
|
+
raise "App has no running instances; try starting it."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
unless console = instances[0].console
|
|
20
|
+
raise "App does not have console access; try restarting it."
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
{ "hostname" => console[:ip],
|
|
24
|
+
"port" => console[:port]
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_credentials
|
|
29
|
+
YAML.load(@app.file("app", "cf-rails-console", ".consoleaccess"))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def start_console
|
|
33
|
+
prompt = login
|
|
34
|
+
|
|
35
|
+
init_readline
|
|
36
|
+
|
|
37
|
+
run_console prompt
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def login(auth = get_credentials)
|
|
41
|
+
if !auth["username"] || !auth["password"]
|
|
42
|
+
raise "Unable to verify console credentials."
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@telnet = telnet_client
|
|
46
|
+
|
|
47
|
+
prompt = nil
|
|
48
|
+
err_msg = "Login attempt timed out."
|
|
49
|
+
|
|
50
|
+
5.times do
|
|
51
|
+
begin
|
|
52
|
+
results = @telnet.login(
|
|
53
|
+
"Name" => auth["username"],
|
|
54
|
+
"Password" => auth["password"])
|
|
55
|
+
|
|
56
|
+
lines = results.sub("Login: Password: ", "").split("\n")
|
|
57
|
+
|
|
58
|
+
last_line = lines.pop
|
|
59
|
+
|
|
60
|
+
if last_line =~ /[$%#>] \z/n
|
|
61
|
+
prompt = last_line
|
|
62
|
+
elsif last_line =~ /Login failed/
|
|
63
|
+
err_msg = last_line
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
rescue TimeoutError
|
|
69
|
+
sleep 1
|
|
70
|
+
|
|
71
|
+
rescue EOFError
|
|
72
|
+
# This may happen if we login right after app starts
|
|
73
|
+
close_console
|
|
74
|
+
sleep 5
|
|
75
|
+
@telnet = telnet_client
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
unless prompt
|
|
80
|
+
close_console
|
|
81
|
+
raise err_msg
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
prompt
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def init_readline
|
|
90
|
+
if Readline.respond_to?("basic_word_break_characters=")
|
|
91
|
+
Readline.basic_word_break_characters= " \t\n`><=;|&{("
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
Readline.completion_append_character = nil
|
|
95
|
+
|
|
96
|
+
# Assumes that sending a String ending with tab will return a non-empty
|
|
97
|
+
# String of comma-separated completion options, terminated by a new line
|
|
98
|
+
# For example, "app.\t" might result in "to_s,nil?,etc\n"
|
|
99
|
+
Readline.completion_proc = proc do |s|
|
|
100
|
+
console_tab_completion_data(s)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def run_console(prompt)
|
|
105
|
+
prev = trap("INT") { |x| exit_console; prev.call(x); exit }
|
|
106
|
+
prev = trap("TERM") { |x| exit_console; prev.call(x); exit }
|
|
107
|
+
|
|
108
|
+
loop do
|
|
109
|
+
cmd = readline_with_history(prompt)
|
|
110
|
+
|
|
111
|
+
if cmd == nil
|
|
112
|
+
exit_console
|
|
113
|
+
break
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
prompt = send_console_command_display_results(cmd, prompt)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def readline_with_history(prompt)
|
|
121
|
+
line = Readline::readline(prompt)
|
|
122
|
+
|
|
123
|
+
return if line == nil || line == 'quit' || line == 'exit'
|
|
124
|
+
|
|
125
|
+
if line !~ /^\s*$/ && Readline::HISTORY.to_a[-1] != line
|
|
126
|
+
Readline::HISTORY.push(line)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
line
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def send_console_command_display_results(cmd, prompt)
|
|
133
|
+
begin
|
|
134
|
+
lines = send_console_command cmd
|
|
135
|
+
|
|
136
|
+
# Assumes the last line is a prompt
|
|
137
|
+
prompt = lines.pop
|
|
138
|
+
|
|
139
|
+
lines.each do |line|
|
|
140
|
+
puts line if line != cmd
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
rescue TimeoutError
|
|
144
|
+
puts "Timed out sending command to server."
|
|
145
|
+
|
|
146
|
+
rescue EOFError
|
|
147
|
+
raise "The console connection has been terminated. Perhaps the app was stopped or deleted?"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
prompt
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def send_console_command(cmd)
|
|
154
|
+
results = @telnet.cmd(cmd)
|
|
155
|
+
results.split("\n")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def exit_console
|
|
159
|
+
@telnet.cmd("String" => "exit", "Timeout" => 1)
|
|
160
|
+
rescue TimeoutError
|
|
161
|
+
# TimeoutError expected, as exit doesn't return anything
|
|
162
|
+
ensure
|
|
163
|
+
close_console
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def close_console
|
|
167
|
+
@telnet.close
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def console_tab_completion_data(cmd)
|
|
171
|
+
begin
|
|
172
|
+
results = @telnet.
|
|
173
|
+
cmd("String" => cmd + "\t", "Match" => /\S*\n$/, "Timeout" => 10)
|
|
174
|
+
results.chomp.split(",")
|
|
175
|
+
rescue TimeoutError
|
|
176
|
+
[] #Just return empty results if timeout occurred on tab completion
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def telnet_client
|
|
181
|
+
Net::Telnet.new(
|
|
182
|
+
"Port" => @port,
|
|
183
|
+
"Prompt" => /[$%#>] \z|Login failed/n,
|
|
184
|
+
"Timeout" => 30,
|
|
185
|
+
"FailEOF" => true)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "cf/plugin"
|
|
2
|
+
require "console/console"
|
|
3
|
+
|
|
4
|
+
module CFConsolePlugin
|
|
5
|
+
class Console < CF::CLI
|
|
6
|
+
desc "Open a console connected to your app"
|
|
7
|
+
group :apps, :manage
|
|
8
|
+
input :app, :argument => :required, :from_given => by_name("app"),
|
|
9
|
+
:desc => "App to connect to"
|
|
10
|
+
input :port, :default => 10000
|
|
11
|
+
def console
|
|
12
|
+
app = input[:app]
|
|
13
|
+
|
|
14
|
+
console = CFConsole.new(client, app)
|
|
15
|
+
port = console.pick_port!(input[:port])
|
|
16
|
+
|
|
17
|
+
with_progress("Opening console on port #{c(port, :name)}") do
|
|
18
|
+
console.open!
|
|
19
|
+
console.wait_for_start
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
console.start_console
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
filter(:start, :start_app) do |app|
|
|
26
|
+
app.console = true
|
|
27
|
+
app
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[](https://travis-ci.org/cloudfoundry/manifests-cf-plugin)
|
|
2
|
+
[](http://badge.fury.io/rb/manifests-cf-plugin)
|
|
3
|
+
[](https://codeclimate.com/github/cloudfoundry/manifests-cf-plugin)
|
|
4
|
+
|
|
5
|
+
## Manifests
|
|
6
|
+
### Info
|
|
7
|
+
With this plugin enabled, any configuration changes you make using the CF `start`, `restart`, `instances`, `logs`, `env`, `health`, `stats`, `scale`, and `app` commands will be saved to a file called *manifest.yml*.
|
|
8
|
+
|
|
9
|
+
### Installation
|
|
10
|
+
```
|
|
11
|
+
gem install manifests-cf-plugin
|
|
12
|
+
```
|
|
13
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "cf/errors"
|
|
2
|
+
|
|
3
|
+
module CFManifests
|
|
4
|
+
class InvalidManifest < CF::UserFriendlyError
|
|
5
|
+
attr_reader :file
|
|
6
|
+
|
|
7
|
+
def initialize(file)
|
|
8
|
+
@file = file
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
"Manifest file '#{@file}' is malformed."
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class CircularDependency < CF::UserFriendlyError
|
|
17
|
+
def initialize(app)
|
|
18
|
+
@app = app
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
"Circular dependency in application '#@app'"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class UnknownSymbol < CF::UserFriendlyError
|
|
27
|
+
def initialize(sym)
|
|
28
|
+
@sym = sym
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
"Undefined symbol in manifest: '#@sym'"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "manifests/loader/builder"
|
|
2
|
+
require "manifests/loader/normalizer"
|
|
3
|
+
require "manifests/loader/resolver"
|
|
4
|
+
|
|
5
|
+
module CFManifests
|
|
6
|
+
class Loader
|
|
7
|
+
include Builder
|
|
8
|
+
include Normalizer
|
|
9
|
+
include Resolver
|
|
10
|
+
|
|
11
|
+
def initialize(file, resolver)
|
|
12
|
+
@file = file
|
|
13
|
+
@resolver = resolver
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def manifest
|
|
17
|
+
info = build(@file)
|
|
18
|
+
normalize! info
|
|
19
|
+
resolve info, @resolver
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# expand a path relative to the manifest file's directory
|
|
25
|
+
def from_manifest(path)
|
|
26
|
+
return path unless @file
|
|
27
|
+
|
|
28
|
+
File.expand_path(path, File.dirname(@file))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "manifests/errors"
|
|
2
|
+
|
|
3
|
+
module CFManifests
|
|
4
|
+
module Builder
|
|
5
|
+
# parse a manifest and merge with its inherited manifests
|
|
6
|
+
def build(file)
|
|
7
|
+
manifest = YAML.load_file file
|
|
8
|
+
raise CFManifests::InvalidManifest.new(file) unless manifest
|
|
9
|
+
|
|
10
|
+
Array(manifest["inherit"]).each do |path|
|
|
11
|
+
manifest = merge_parent(path, manifest)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
manifest.delete("inherit")
|
|
15
|
+
|
|
16
|
+
manifest
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# merge the manifest at `parent_path' into the `child'
|
|
22
|
+
def merge_parent(parent_path, child)
|
|
23
|
+
merge_manifest(build(from_manifest(parent_path)), child)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# deep hash merge
|
|
27
|
+
def merge_manifest(parent, child)
|
|
28
|
+
merge = proc do |_, old, new|
|
|
29
|
+
if new.is_a?(Hash) && old.is_a?(Hash)
|
|
30
|
+
old.merge(new, &merge)
|
|
31
|
+
else
|
|
32
|
+
new
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
parent.merge(child, &merge)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
module CFManifests
|
|
2
|
+
module Normalizer
|
|
3
|
+
MANIFEST_META = ["applications", "properties"]
|
|
4
|
+
|
|
5
|
+
def normalize!(manifest)
|
|
6
|
+
toplevel = toplevel_attributes(manifest)
|
|
7
|
+
|
|
8
|
+
apps = manifest["applications"]
|
|
9
|
+
apps ||= [{}]
|
|
10
|
+
|
|
11
|
+
default_paths_to_keys!(apps)
|
|
12
|
+
|
|
13
|
+
apps = convert_to_array(apps)
|
|
14
|
+
|
|
15
|
+
merge_toplevel!(toplevel, manifest, apps)
|
|
16
|
+
normalize_apps!(apps)
|
|
17
|
+
|
|
18
|
+
manifest["applications"] = apps
|
|
19
|
+
|
|
20
|
+
normalize_paths!(apps)
|
|
21
|
+
|
|
22
|
+
keyval = normalize_key_val(manifest)
|
|
23
|
+
manifest.clear.merge!(keyval)
|
|
24
|
+
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def normalize_paths!(apps)
|
|
31
|
+
apps.each do |app|
|
|
32
|
+
app["path"] = from_manifest(app["path"])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def convert_to_array(apps)
|
|
37
|
+
return apps if apps.is_a?(Array)
|
|
38
|
+
|
|
39
|
+
ordered_by_deps(apps)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# sort applications in dependency order
|
|
43
|
+
# e.g. if A depends on B, B will be listed before A
|
|
44
|
+
def ordered_by_deps(apps, processed = Set[])
|
|
45
|
+
ordered = []
|
|
46
|
+
apps.each do |tag, info|
|
|
47
|
+
next if processed.include?(tag)
|
|
48
|
+
|
|
49
|
+
if deps = Array(info["depends-on"])
|
|
50
|
+
dep_apps = {}
|
|
51
|
+
deps.each do |dep|
|
|
52
|
+
dep_apps[dep] = apps[dep]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
processed.add(tag)
|
|
56
|
+
|
|
57
|
+
ordered += ordered_by_deps(dep_apps, processed)
|
|
58
|
+
ordered << info
|
|
59
|
+
else
|
|
60
|
+
ordered << info
|
|
61
|
+
processed.add(tag)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
ordered.each { |app| app.delete("depends-on") }
|
|
66
|
+
|
|
67
|
+
ordered
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def default_paths_to_keys!(apps)
|
|
71
|
+
return if apps.is_a?(Array)
|
|
72
|
+
|
|
73
|
+
apps.each do |tag, app|
|
|
74
|
+
app["path"] ||= tag
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def normalize_apps!(apps)
|
|
79
|
+
apps.each do |app|
|
|
80
|
+
normalize_app!(app)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def merge_toplevel!(toplevel, manifest, apps)
|
|
85
|
+
return if toplevel.empty?
|
|
86
|
+
|
|
87
|
+
apps.collect! do |a|
|
|
88
|
+
toplevel.merge(a)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
toplevel.each do |k, _|
|
|
92
|
+
manifest.delete k
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def normalize_app!(app)
|
|
97
|
+
if app.key?("mem")
|
|
98
|
+
app["memory"] = app.delete("mem")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if app.key?("url") && app["url"].nil?
|
|
102
|
+
app["url"] = "none"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if app.key?("subdomain")
|
|
106
|
+
if app.key?("host")
|
|
107
|
+
app.delete("subdomain")
|
|
108
|
+
else
|
|
109
|
+
app["host"] = app.delete("subdomain")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def toplevel_attributes(manifest)
|
|
115
|
+
top =
|
|
116
|
+
manifest.reject { |k, _|
|
|
117
|
+
MANIFEST_META.include? k
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# implicit toplevel path of .
|
|
121
|
+
top["path"] ||= "."
|
|
122
|
+
|
|
123
|
+
top
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def normalize_key_val(val)
|
|
127
|
+
case val
|
|
128
|
+
when Hash
|
|
129
|
+
stringified = {}
|
|
130
|
+
|
|
131
|
+
val.each do |k, v|
|
|
132
|
+
stringified[k.to_sym] = normalize_key_val(v)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
stringified
|
|
136
|
+
when Array
|
|
137
|
+
val.collect { |x| normalize_key_val(x) }
|
|
138
|
+
when nil
|
|
139
|
+
nil
|
|
140
|
+
else
|
|
141
|
+
val.to_s
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|