cf 0.6.1.rc9 → 0.6.1.rc10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/lib/cf.rb +4 -0
  2. data/lib/cf/cli.rb +8 -3
  3. data/lib/cf/version.rb +1 -1
  4. data/lib/console/README.md +16 -0
  5. data/lib/console/console.rb +187 -0
  6. data/lib/console/plugin.rb +30 -0
  7. data/lib/manifests/README.md +13 -0
  8. data/lib/manifests/errors.rb +35 -0
  9. data/lib/manifests/loader.rb +31 -0
  10. data/lib/manifests/loader/builder.rb +39 -0
  11. data/lib/manifests/loader/normalizer.rb +145 -0
  12. data/lib/manifests/loader/resolver.rb +79 -0
  13. data/lib/manifests/manifests.rb +304 -0
  14. data/lib/manifests/plugin.rb +141 -0
  15. data/lib/tunnel/README.md +29 -0
  16. data/lib/tunnel/helper-app/Gemfile +11 -0
  17. data/lib/tunnel/helper-app/Gemfile.lock +48 -0
  18. data/lib/tunnel/helper-app/server.rb +43 -0
  19. data/lib/tunnel/plugin.rb +183 -0
  20. data/lib/tunnel/tunnel.rb +305 -0
  21. data/spec/assets/rails328_ruby187_app/Gemfile +39 -0
  22. data/spec/assets/rails328_ruby187_app/README.rdoc +261 -0
  23. data/spec/assets/rails328_ruby187_app/Rakefile +7 -0
  24. data/spec/assets/rails328_ruby187_app/app/assets/images/rails.png +0 -0
  25. data/spec/assets/rails328_ruby187_app/app/assets/javascripts/application.js +15 -0
  26. data/spec/assets/rails328_ruby187_app/app/assets/stylesheets/application.css +13 -0
  27. data/spec/assets/rails328_ruby187_app/app/controllers/application_controller.rb +3 -0
  28. data/spec/assets/rails328_ruby187_app/app/helpers/application_helper.rb +2 -0
  29. data/spec/assets/rails328_ruby187_app/app/views/layouts/application.html.erb +14 -0
  30. data/spec/assets/rails328_ruby187_app/config.ru +4 -0
  31. data/spec/assets/rails328_ruby187_app/config/application.rb +62 -0
  32. data/spec/assets/rails328_ruby187_app/config/boot.rb +6 -0
  33. data/spec/assets/rails328_ruby187_app/config/database.yml +25 -0
  34. data/spec/assets/rails328_ruby187_app/config/environment.rb +5 -0
  35. data/spec/assets/rails328_ruby187_app/config/environments/development.rb +37 -0
  36. data/spec/assets/rails328_ruby187_app/config/environments/production.rb +67 -0
  37. data/spec/assets/rails328_ruby187_app/config/environments/test.rb +37 -0
  38. data/spec/assets/rails328_ruby187_app/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/assets/rails328_ruby187_app/config/initializers/inflections.rb +15 -0
  40. data/spec/assets/rails328_ruby187_app/config/initializers/mime_types.rb +5 -0
  41. data/spec/assets/rails328_ruby187_app/config/initializers/secret_token.rb +7 -0
  42. data/spec/assets/rails328_ruby187_app/config/initializers/session_store.rb +8 -0
  43. data/spec/assets/rails328_ruby187_app/config/initializers/wrap_parameters.rb +14 -0
  44. data/spec/assets/rails328_ruby187_app/config/locales/en.yml +5 -0
  45. data/spec/assets/rails328_ruby187_app/config/routes.rb +58 -0
  46. data/spec/assets/rails328_ruby187_app/db/seeds.rb +7 -0
  47. data/spec/assets/rails328_ruby187_app/doc/README_FOR_APP +2 -0
  48. data/spec/assets/rails328_ruby187_app/manifest.yml +7 -0
  49. data/spec/assets/rails328_ruby187_app/public/404.html +26 -0
  50. data/spec/assets/rails328_ruby187_app/public/422.html +26 -0
  51. data/spec/assets/rails328_ruby187_app/public/500.html +25 -0
  52. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css +0 -0
  53. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css.gz +0 -0
  54. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js +17 -0
  55. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js.gz +0 -0
  56. data/spec/assets/rails328_ruby187_app/public/assets/application.css +0 -0
  57. data/spec/assets/rails328_ruby187_app/public/assets/application.css.gz +0 -0
  58. data/spec/assets/rails328_ruby187_app/public/assets/application.js +17 -0
  59. data/spec/assets/rails328_ruby187_app/public/assets/application.js.gz +0 -0
  60. data/spec/assets/rails328_ruby187_app/public/assets/manifest.yml +4 -0
  61. data/spec/assets/rails328_ruby187_app/public/assets/rails-be8732dac73d845ac5b142c8fb5f9fb0.png +0 -0
  62. data/spec/assets/rails328_ruby187_app/public/assets/rails.png +0 -0
  63. data/spec/assets/rails328_ruby187_app/public/favicon.ico +0 -0
  64. data/spec/assets/rails328_ruby187_app/public/index.html +241 -0
  65. data/spec/assets/rails328_ruby187_app/public/robots.txt +5 -0
  66. data/spec/assets/rails328_ruby187_app/script/rails +6 -0
  67. data/spec/assets/rails328_ruby187_app/test/performance/browsing_test.rb +12 -0
  68. data/spec/assets/rails328_ruby187_app/test/test_helper.rb +13 -0
  69. data/spec/cf/cli/app/stats_spec.rb +20 -20
  70. data/spec/cf/cli_spec.rb +37 -18
  71. data/spec/console/console_spec.rb +189 -0
  72. data/spec/features/push_flow_spec.rb +1 -1
  73. data/spec/manifests/errors_spec.rb +43 -0
  74. data/spec/manifests/loader/builder_spec.rb +80 -0
  75. data/spec/manifests/loader/normalizer_spec.rb +158 -0
  76. data/spec/manifests/manifests_spec.rb +309 -0
  77. data/spec/manifests/plugin_spec.rb +362 -0
  78. data/spec/tunnel/plugin_spec.rb +31 -0
  79. metadata +184 -25
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 :proxy, :desc => "Run this command as another user (admin)", :alias => "-u",
30
- :value => :email
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
@@ -1,3 +1,3 @@
1
1
  module CF
2
- VERSION = "0.6.1.rc9".freeze
2
+ VERSION = "0.6.1.rc10".freeze
3
3
  end
@@ -0,0 +1,16 @@
1
+ [![Build Status](https://travis-ci.org/cloudfoundry/console-cf-plugin.png)](https://travis-ci.org/cloudfoundry/console-cf-plugin)
2
+ [![Gem Version](https://badge.fury.io/rb/console-cf-plugin.png)](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
+ [![Build Status](https://travis-ci.org/cloudfoundry/manifests-cf-plugin.png)](https://travis-ci.org/cloudfoundry/manifests-cf-plugin)
2
+ [![Gem Version](https://badge.fury.io/rb/manifests-cf-plugin.png)](http://badge.fury.io/rb/manifests-cf-plugin)
3
+ [![Code Climate](https://codeclimate.com/github/cloudfoundry/manifests-cf-plugin.png)](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