publish_to_web 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/Guardfile +82 -0
- data/LICENSE +8 -0
- data/README.md +19 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/publish_to_web.rb +131 -0
- data/lib/publish_to_web/config.rb +32 -0
- data/lib/publish_to_web/directory.rb +149 -0
- data/lib/publish_to_web/tunnel.rb +60 -0
- data/lib/publish_to_web/version.rb +3 -0
- data/publish_to_web.gemspec +36 -0
- metadata +228 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eccc7c3644753fc76026402d3a20da43e5352b9d
|
4
|
+
data.tar.gz: 8a9fcad04e05dc7f0cdcc43c806d8c54d5538ac9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6cf3e69ccbe941e851c9e04b89c03ce318964aadcfe3ee522c18bbcd76220bb3eb902211fd0ec60e87afa1bbecdd911f1d3c211a537fdc87837ce0183c13f736
|
7
|
+
data.tar.gz: a2907049c62ad81986d3c845dede798c31f0ff89e0bde9a4c619523867123b5037c35c97721090006cf2031b86e3521259a3be0c0f03110322d274c2e5cd716c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
28
|
+
require "guard/rspec/dsl"
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
30
|
+
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
32
|
+
|
33
|
+
# RSpec files
|
34
|
+
rspec = dsl.rspec
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
37
|
+
watch(rspec.spec_files)
|
38
|
+
|
39
|
+
# Ruby files
|
40
|
+
ruby = dsl.ruby
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
42
|
+
|
43
|
+
# Rails files
|
44
|
+
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
45
|
+
dsl.watch_spec_files_for(rails.app_files)
|
46
|
+
dsl.watch_spec_files_for(rails.views)
|
47
|
+
|
48
|
+
watch(rails.controllers) do |m|
|
49
|
+
[
|
50
|
+
rspec.spec.("routing/#{m[1]}_routing"),
|
51
|
+
rspec.spec.("controllers/#{m[1]}_controller"),
|
52
|
+
rspec.spec.("acceptance/#{m[1]}")
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Rails config changes
|
57
|
+
watch(rails.spec_helper) { rspec.spec_dir }
|
58
|
+
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
59
|
+
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
60
|
+
|
61
|
+
# Capybara features specs
|
62
|
+
watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
|
63
|
+
watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") }
|
64
|
+
|
65
|
+
# Turnip features and steps
|
66
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
67
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
68
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
guard :bundler do
|
73
|
+
require 'guard/bundler'
|
74
|
+
require 'guard/bundler/verify'
|
75
|
+
helper = Guard::Bundler::Verify.new
|
76
|
+
|
77
|
+
files = ['Gemfile']
|
78
|
+
files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
|
79
|
+
|
80
|
+
# Assume files are symlinked from somewhere
|
81
|
+
files.each { |file| watch(helper.real_path(file)) }
|
82
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
Copyright (c) 2016 Protonet GmbH
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
5
|
+
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
7
|
+
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# PublishToWeb [![Build Status](https://travis-ci.org/protonet/publish_to_web.svg?branch=master)](https://travis-ci.org/protonet/publish_to_web)
|
2
|
+
|
3
|
+
This enables any Protonet or Experimental Platform system to get a public URL at NAME.protonet.info via the Protonet Proxy Tunnel service.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
gem install publish_to_web
|
8
|
+
ruby -e "require 'publish_to_web'; PublishToWeb.start"
|
9
|
+
|
10
|
+
## Development
|
11
|
+
|
12
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
13
|
+
|
14
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
15
|
+
|
16
|
+
## Contributing
|
17
|
+
|
18
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/protonet/publish_to_web.
|
19
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "publish_to_web"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Standard Library
|
2
|
+
require 'json'
|
3
|
+
require 'logger'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'socket'
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
# Gems
|
9
|
+
require 'net/ssh'
|
10
|
+
require 'http'
|
11
|
+
require 'rainbow'
|
12
|
+
require 'platform-skvs'
|
13
|
+
|
14
|
+
# Library
|
15
|
+
require "publish_to_web/config"
|
16
|
+
require "publish_to_web/directory"
|
17
|
+
require "publish_to_web/tunnel"
|
18
|
+
require "publish_to_web/version"
|
19
|
+
|
20
|
+
class PublishToWeb
|
21
|
+
def self.create_logger
|
22
|
+
Logger.new(STDOUT).tap do |logger|
|
23
|
+
logger.level = Logger::INFO
|
24
|
+
logger.formatter = -> (severity, datetime, progname, msg) do
|
25
|
+
color = {
|
26
|
+
"WARN" => :yellow,
|
27
|
+
"ERROR" => :red,
|
28
|
+
"FATAL" => :red
|
29
|
+
}[severity] || :white
|
30
|
+
Rainbow("[#{datetime}][#{severity.ljust(5)}] #{msg}\n").color(color)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :forward_port, :bind_host, :proxy_host,
|
36
|
+
:proxy_user, :proxy_port, :directory_host, :logger, :config
|
37
|
+
|
38
|
+
def initialize(
|
39
|
+
forward_port: 80,
|
40
|
+
bind_host: "127.0.0.1",
|
41
|
+
proxy_host: "proxy.protonet.info",
|
42
|
+
proxy_user: "localtunnel",
|
43
|
+
proxy_port: 22666,
|
44
|
+
directory_host: "https://directory.protonet.info",
|
45
|
+
config: Config.new,
|
46
|
+
logger: self.class.create_logger
|
47
|
+
)
|
48
|
+
|
49
|
+
@forward_port = forward_port
|
50
|
+
@bind_host = bind_host
|
51
|
+
@proxy_host = proxy_host
|
52
|
+
@proxy_user = proxy_user
|
53
|
+
@proxy_port = proxy_port
|
54
|
+
@directory_host = directory_host
|
55
|
+
@config = config
|
56
|
+
@logger = logger
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_local_endpoint
|
60
|
+
logger.info "Checking if local backend is available at #{bind_host}:#{forward_port}"
|
61
|
+
TCPSocket.new(bind_host, forward_port).close
|
62
|
+
|
63
|
+
rescue Errno::ECONNREFUSED => err
|
64
|
+
logger.warn "Local backend is not available (yet?) - waiting for it to become available"
|
65
|
+
sleep 5
|
66
|
+
check_local_endpoint
|
67
|
+
end
|
68
|
+
|
69
|
+
def prepare_directory
|
70
|
+
if node_name = config.node_name
|
71
|
+
directory.set_node_name node_name
|
72
|
+
end
|
73
|
+
directory.set_version
|
74
|
+
directory.public_key
|
75
|
+
end
|
76
|
+
|
77
|
+
def start_tunnel
|
78
|
+
config.success = config.error = nil
|
79
|
+
|
80
|
+
prepare_directory
|
81
|
+
|
82
|
+
check_local_endpoint
|
83
|
+
|
84
|
+
logger.info "Starting tunnel to #{proxy_host} as #{directory.node_name}"
|
85
|
+
tunnel.start do
|
86
|
+
config.success = "connection_established"
|
87
|
+
end
|
88
|
+
|
89
|
+
rescue Net::SSH::AuthenticationFailed => err
|
90
|
+
logger.warn "#{err.class}: #{err}"
|
91
|
+
logger.warn "Probably the SSH key is not deployed on the proxy server yet, retrying in a bit"
|
92
|
+
|
93
|
+
sleep 30
|
94
|
+
start_tunnel
|
95
|
+
|
96
|
+
rescue Errno::ECONNREFUSED => err
|
97
|
+
logger.warn "#{err.class}: #{err}"
|
98
|
+
logger.warn "Local backend connection failed (on port #{forward_port}) - retrying"
|
99
|
+
|
100
|
+
start_tunnel
|
101
|
+
|
102
|
+
rescue PublishToWeb::Directory::HttpResponseError => err
|
103
|
+
logger.warn "#{err.class}: #{err}"
|
104
|
+
logger.warn "Failed to interact with directory, will try again in a bit"
|
105
|
+
|
106
|
+
# Write out that we have an issue since the directory might refuse
|
107
|
+
# our license, our chosen node name might be in conflict and so on
|
108
|
+
config.success = nil
|
109
|
+
config.error = "directory_failure.#{err.response.status.to_i}"
|
110
|
+
|
111
|
+
sleep 30
|
112
|
+
start_tunnel
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def directory
|
118
|
+
@directory ||= Directory.new host: directory_host, logger: logger, config: config
|
119
|
+
end
|
120
|
+
|
121
|
+
def tunnel
|
122
|
+
@tunnel ||= Tunnel.new proxy_host: proxy_host,
|
123
|
+
proxy_user: proxy_user,
|
124
|
+
proxy_port: proxy_port,
|
125
|
+
identity: directory.private_key,
|
126
|
+
bind_host: bind_host,
|
127
|
+
remote_port: directory.remote_port,
|
128
|
+
forward_port: forward_port,
|
129
|
+
logger: self.class.create_logger
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class PublishToWeb
|
2
|
+
class Config
|
3
|
+
attr_reader :store
|
4
|
+
private :store
|
5
|
+
|
6
|
+
def self.config_attribute(name, key)
|
7
|
+
define_method name do
|
8
|
+
store.get key
|
9
|
+
end
|
10
|
+
|
11
|
+
define_method "#{name}=" do |value|
|
12
|
+
store.set key, value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(store: SKVS)
|
17
|
+
@store = store
|
18
|
+
end
|
19
|
+
|
20
|
+
def enabled?
|
21
|
+
!!store.get('ptw/enabled')
|
22
|
+
end
|
23
|
+
|
24
|
+
config_attribute :hardware_id, "ptw/hardware_id"
|
25
|
+
config_attribute :license_key, "ptw/license"
|
26
|
+
config_attribute :node_name, "hostname"
|
27
|
+
config_attribute :private_key, "ptw/publish_to_web_key_private"
|
28
|
+
config_attribute :public_key, "ptw/publish_to_web_key_public"
|
29
|
+
config_attribute :success, "ptw/success"
|
30
|
+
config_attribute :error, "ptw/error"
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'sshkey'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'shellwords'
|
5
|
+
|
6
|
+
class PublishToWeb
|
7
|
+
class Directory
|
8
|
+
class HttpResponseError < StandardError
|
9
|
+
attr_reader :response
|
10
|
+
def initialize(message, response)
|
11
|
+
@response = response
|
12
|
+
super "#{message} - HTTP Status: #{response.status}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :host, :config, :logger
|
17
|
+
|
18
|
+
def initialize(host:, config:, logger:)
|
19
|
+
@host = host
|
20
|
+
@config = config
|
21
|
+
@logger = logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def remote_port
|
25
|
+
info["port"] if info
|
26
|
+
end
|
27
|
+
|
28
|
+
def directory_public_key_sha1
|
29
|
+
info["pubkey_sha1"] if info
|
30
|
+
end
|
31
|
+
|
32
|
+
def node_name
|
33
|
+
info["node_name"] if info
|
34
|
+
end
|
35
|
+
|
36
|
+
def private_key
|
37
|
+
ensure_valid_identity!
|
38
|
+
config.private_key
|
39
|
+
end
|
40
|
+
|
41
|
+
def public_key
|
42
|
+
ensure_valid_identity!
|
43
|
+
config.public_key
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the stored hardware_id or generates a new one
|
47
|
+
def hardware_id
|
48
|
+
config.hardware_id ||= "aal-#{SecureRandom.uuid}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the stored license_key or requests a new one
|
52
|
+
def license_key
|
53
|
+
config.license_key ||= create_license_key
|
54
|
+
end
|
55
|
+
|
56
|
+
def version
|
57
|
+
"platform-alpha"
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_node_name(node_name)
|
61
|
+
logger.info "Setting node name at directory to #{node_name}"
|
62
|
+
response = HTTP.post url_for('set_node_name'), form: { license_key: license_key, node_name: node_name }
|
63
|
+
if (200..299).include? response.status
|
64
|
+
logger.info "New node name registered successfully"
|
65
|
+
info refresh: true
|
66
|
+
true
|
67
|
+
else
|
68
|
+
raise HttpResponseError.new("Failed to set new node name in directory", response)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_version
|
73
|
+
logger.info "Setting version at directory to #{version}"
|
74
|
+
response = HTTP.post url_for('set_version'), form: { license_key: license_key, version: Shellwords.shellescape(version) }
|
75
|
+
if (200..299).include? response.status
|
76
|
+
true
|
77
|
+
else
|
78
|
+
raise HttpResponseError.new("Failed to set version in directory", response)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def public_key_ok?
|
85
|
+
if config.public_key and config.private_key
|
86
|
+
SSHKey.new(config.private_key).ssh_public_key == config.public_key &&
|
87
|
+
Digest::SHA1.hexdigest(config.public_key) == directory_public_key_sha1
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def ensure_valid_identity!
|
92
|
+
logger.info "Checking for useable SSH key pair"
|
93
|
+
if public_key_ok?
|
94
|
+
logger.info "The existing SSH key pair appears to be valid"
|
95
|
+
true
|
96
|
+
else
|
97
|
+
logger.info "Generating new SSH key pair"
|
98
|
+
SSHKey.generate(type: 'rsa', bits: 4096).tap do |new_identity|
|
99
|
+
register_identity new_identity.ssh_public_key
|
100
|
+
info refresh: true
|
101
|
+
|
102
|
+
config.private_key = new_identity.private_key
|
103
|
+
config.public_key = new_identity.ssh_public_key
|
104
|
+
end
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def info(refresh: false)
|
110
|
+
@info = nil if refresh
|
111
|
+
|
112
|
+
@info ||= begin
|
113
|
+
logger.info "Retrieving connection info from directory #{host}"
|
114
|
+
response = HTTP.get(url_for('info'), params: { license_key: license_key })
|
115
|
+
if response.status == 200
|
116
|
+
JSON.load(response.body)
|
117
|
+
else
|
118
|
+
raise HttpResponseError.new("Failed to get connection info from directory", response)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def create_license_key
|
124
|
+
logger.info "Creating a new license key in directory"
|
125
|
+
response = HTTP.get url_for('create_license'), params: { hardware_id: hardware_id }
|
126
|
+
if (200..299).include? response.status
|
127
|
+
logger.info "Successfully created new license key"
|
128
|
+
JSON.parse(response.body)["license_key"]
|
129
|
+
else
|
130
|
+
raise HttpResponseError.new("Failed to create license in directory", response)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def register_identity(new_public_key)
|
135
|
+
logger.info "Registering new public key in directory"
|
136
|
+
response = HTTP.post url_for("set_public_key"), form: { license_key: license_key, public_key: new_public_key }
|
137
|
+
if (200..299).include? response.status
|
138
|
+
logger.info "Successfully registered new public key in directory"
|
139
|
+
true
|
140
|
+
else
|
141
|
+
raise HttpResponseError.new("Failed to register identity with directory", response)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def url_for(path)
|
146
|
+
File.join host, path
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class PublishToWeb
|
2
|
+
class Tunnel
|
3
|
+
attr_reader :proxy_host, :proxy_user, :proxy_port, :identity, :bind_host, :remote_port, :forward_port, :logger
|
4
|
+
|
5
|
+
def initialize(proxy_host:, proxy_user:, proxy_port:, identity:, bind_host:, remote_port:, forward_port:, logger:)
|
6
|
+
@proxy_host = proxy_host
|
7
|
+
@proxy_user = proxy_user
|
8
|
+
@proxy_port = proxy_port
|
9
|
+
@identity = identity
|
10
|
+
@bind_host = bind_host
|
11
|
+
@remote_port = remote_port
|
12
|
+
@forward_port = forward_port
|
13
|
+
@logger = logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def ssh_options
|
17
|
+
@ssh_options ||= {
|
18
|
+
keepalive_interval: 5,
|
19
|
+
paranoid: false,
|
20
|
+
# ExitOnForwardFailure ??
|
21
|
+
use_agent: false,
|
22
|
+
user_known_hosts_file: "/dev/null",
|
23
|
+
port: proxy_port,
|
24
|
+
key_data: [identity],
|
25
|
+
# We need to make another logger here because verbose: :warn
|
26
|
+
# will change the log level on the logger - we want to keep
|
27
|
+
# the info messages from the client in general but avoid the
|
28
|
+
# low-level noise from net/ssh
|
29
|
+
logger: PublishToWeb.create_logger,
|
30
|
+
verbose: :warn
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def local_port
|
35
|
+
@local_port ||= begin
|
36
|
+
server = TCPServer.new('127.0.0.1', 0)
|
37
|
+
local_port = server.addr[1]
|
38
|
+
server.close
|
39
|
+
local_port
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def start
|
44
|
+
Net::SSH.start proxy_host, proxy_user, ssh_options do |ssh|
|
45
|
+
ssh.forward.remote forward_port, bind_host, remote_port do |real_remote_port|
|
46
|
+
# Indicate to our caller that we have established the connection successfully
|
47
|
+
yield if block_given?
|
48
|
+
logger.info "Established remote forwarding at port #{real_remote_port}"
|
49
|
+
end
|
50
|
+
|
51
|
+
ssh.forward.local(local_port, bind_host, 8765).tap do |real_local_port|
|
52
|
+
logger.info "Established local forwarding at port #{real_local_port}"
|
53
|
+
end
|
54
|
+
logger.info "Entering keepalive loop"
|
55
|
+
|
56
|
+
ssh.loop { true }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'publish_to_web/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "publish_to_web"
|
8
|
+
spec.version = PublishToWeb::VERSION
|
9
|
+
spec.authors = ["Christoph Olszowka"]
|
10
|
+
spec.email = ["christoph@olszowka.de"]
|
11
|
+
|
12
|
+
spec.summary = %q{Handle Protonet PublishToWeb Connection}
|
13
|
+
spec.description = %q{Handle Protonet PublishToWeb Connection}
|
14
|
+
spec.homepage = "https://github.com/protonet/publish_to_web"
|
15
|
+
|
16
|
+
spec.licenses = ['MIT']
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency "net-ssh", "~> 3.0"
|
24
|
+
spec.add_runtime_dependency "http", "~> 1.0"
|
25
|
+
spec.add_runtime_dependency "sshkey", "~> 1.8.0"
|
26
|
+
spec.add_runtime_dependency "platform-skvs", "~> 0.2.0"
|
27
|
+
spec.add_runtime_dependency "rainbow", "~> 2.0"
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
32
|
+
spec.add_development_dependency "webmock", "~> 1.22"
|
33
|
+
spec.add_development_dependency "simplecov"
|
34
|
+
spec.add_development_dependency "guard-rspec", "~> 4.6"
|
35
|
+
spec.add_development_dependency "guard-bundler", "~> 2.1"
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: publish_to_web
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christoph Olszowka
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: net-ssh
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: http
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sshkey
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.8.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.8.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: platform-skvs
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.2.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rainbow
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.11'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.11'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.22'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.22'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: guard-rspec
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '4.6'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '4.6'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: guard-bundler
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '2.1'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '2.1'
|
181
|
+
description: Handle Protonet PublishToWeb Connection
|
182
|
+
email:
|
183
|
+
- christoph@olszowka.de
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- ".gitignore"
|
189
|
+
- ".rspec"
|
190
|
+
- ".travis.yml"
|
191
|
+
- Gemfile
|
192
|
+
- Guardfile
|
193
|
+
- LICENSE
|
194
|
+
- README.md
|
195
|
+
- Rakefile
|
196
|
+
- bin/console
|
197
|
+
- bin/setup
|
198
|
+
- lib/publish_to_web.rb
|
199
|
+
- lib/publish_to_web/config.rb
|
200
|
+
- lib/publish_to_web/directory.rb
|
201
|
+
- lib/publish_to_web/tunnel.rb
|
202
|
+
- lib/publish_to_web/version.rb
|
203
|
+
- publish_to_web.gemspec
|
204
|
+
homepage: https://github.com/protonet/publish_to_web
|
205
|
+
licenses:
|
206
|
+
- MIT
|
207
|
+
metadata: {}
|
208
|
+
post_install_message:
|
209
|
+
rdoc_options: []
|
210
|
+
require_paths:
|
211
|
+
- lib
|
212
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
213
|
+
requirements:
|
214
|
+
- - ">="
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '0'
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - ">="
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
222
|
+
requirements: []
|
223
|
+
rubyforge_project:
|
224
|
+
rubygems_version: 2.5.1
|
225
|
+
signing_key:
|
226
|
+
specification_version: 4
|
227
|
+
summary: Handle Protonet PublishToWeb Connection
|
228
|
+
test_files: []
|