tunl 0.0.1

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.
Files changed (3) hide show
  1. data/bin/tunl +91 -0
  2. data/lib/tunl.rb +204 -0
  3. metadata +63 -0
data/bin/tunl ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/tunl'
4
+ require 'thor'
5
+
6
+ module Tunl
7
+
8
+ class CLI < Thor
9
+
10
+ desc "start <name>", "launch named tunnel"
11
+ method_options :check => :string, :port => :string, :verbose => :boolean
12
+ def start(name)
13
+ printf("\n%-3s %-8s %-20s\n\n", '~~>', "Start", "Tunnel: #{name}")
14
+ tunl = Tunl::Base.new
15
+ tunl.setup_security_group(name)
16
+ tunl.setup_key_pair(name)
17
+ tunl.setup_instance(name)
18
+
19
+ instance = tunl.find_instance(name)
20
+ port = options[:port] || 3000
21
+ if options[:verbose]
22
+ verbose = "-v"
23
+ else
24
+ verbose = ''
25
+ end
26
+
27
+ if uri = options[:check]
28
+ tunl.log("check", "#{instance[:dns_name]}#{uri}")
29
+ t = Thread.new do
30
+ loop do
31
+ `curl http://#{instance[:dns_name]}#{uri} > /dev/null 2>&1`
32
+ puts "."
33
+ sleep 60
34
+ end
35
+ end
36
+ end
37
+ tunl.log("tunnel", "#{instance[:dns_name]}:80 -> localhost:#{port}")
38
+ puts "\n\n"
39
+ `ssh -i #{tunl.key_pair_filepath(name)} #{verbose} -N -p 22 root@#{instance[:dns_name]} -R *:80:localhost:#{port}`
40
+ end
41
+
42
+ desc "ssh <name>", "ssh into the named instance"
43
+ def ssh(name)
44
+ tunl = Tunl::Base.new
45
+ instance = tunl.find_instance(name)
46
+ if instance.nil?
47
+ puts "Tunl #{name} does not exist"
48
+ exit(0)
49
+ end
50
+ exec "ssh -i #{tunl.key_pair_filepath(name)} root@#{instance[:dns_name]}"
51
+ end
52
+
53
+ desc "stop <name>", "terminate the named tunnel"
54
+ def stop(name)
55
+ tunl = Tunl::Base.new
56
+ if instance = tunl.find_instance(name)
57
+ printf("\n%-3s %-8s %-20s\n\n", '~~>', "Stop", "Tunnel: #{name}")
58
+ tunl.delete_instance(name)
59
+ tunl.delete_security_group(name)
60
+ tunl.delete_key_pair(name)
61
+ puts ""
62
+ else
63
+ puts "tunl #{name} not running"
64
+ end
65
+ end
66
+
67
+ desc "list", "list all the tunnels"
68
+ def list
69
+ tunl = Tunl::Base.new
70
+ instances = tunl.find_all_instances
71
+ if instances.size==0
72
+ puts "no running tunnels"
73
+ else
74
+ $stdout.printf("\n%-3s %-12s\n\n", '', 'Tunnels')
75
+ $stdout.printf("%-3s %-12s %-12s %-12s\n\n", '', 'Name', 'Aws Id', 'Host')
76
+ instances.each do |item|
77
+ $stdout.printf("%-3s %-12s %-12s %-12s\n", '', item[:aws_groups].first.gsub('tunl_', ''), item[:aws_instance_id], item[:dns_name])
78
+ end
79
+ $stdout.puts("\n\n")
80
+ end
81
+ end
82
+
83
+ no_tasks do
84
+
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ Tunl::CLI.start
data/lib/tunl.rb ADDED
@@ -0,0 +1,204 @@
1
+
2
+ # ruby requirements
3
+ require 'pathname'
4
+ require 'fileutils'
5
+
6
+
7
+ require 'net/http'
8
+ # hack to disable SSL warnings issued by the right_http_connection gem
9
+ class Net::HTTP
10
+ alias_method :old_initialize, :initialize
11
+ def initialize(*args)
12
+ old_initialize(*args)
13
+ @ssl_context = OpenSSL::SSL::SSLContext.new
14
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
+ end
16
+ end
17
+
18
+
19
+ # gem requirements
20
+ require 'right_aws'
21
+
22
+ # hack to supress annoying log messages from right_aws gem
23
+ module Tunl
24
+ class SilentLogger
25
+ def self.info(msg)
26
+ end
27
+ def self.error(msg)
28
+ puts "RIGHT AWS ERROR : #{msg}"
29
+ end
30
+ def self.warn(msg)
31
+ puts "RIGHT AWS WARN: #{msg}"
32
+ end
33
+ end
34
+ end
35
+
36
+ module Tunl
37
+ class Base
38
+
39
+ def root
40
+ @root ||= Pathname.new(File.expand_path("~/.tunl"))
41
+ end
42
+
43
+ def config
44
+ @config ||= YAML.load(File.read(root.join('tunl.yml')))
45
+ end
46
+
47
+ def ec2
48
+ @ec2 ||= RightAws::Ec2.new(config['aws_access_key'],config['aws_secret_key'], :logger => SilentLogger)
49
+ end
50
+
51
+ def key_pair_filepath(name)
52
+ root.join("tunl_key_pair_#{name}")
53
+ end
54
+
55
+ def create_key_pair(name)
56
+ log "create", "key pair tunl_#{name}"
57
+ filepath = key_pair_filepath(name)
58
+ key_pair = ec2.create_key_pair("tunl_#{name}")
59
+ File.open(filepath, 'w') { |f| f.write(key_pair[:aws_material]) }
60
+ FileUtils.chmod 0400, filepath
61
+ rescue RightAws::AwsError => e
62
+ if e.message =~ /already exists/
63
+ # WARNING : this could be dangerous.
64
+ delete_key_pair(name)
65
+ retry
66
+ else
67
+ raise e
68
+ end
69
+ end
70
+
71
+ def delete_key_pair(name)
72
+ log "delete", "key Pair tunl_#{name}"
73
+ # delete key pair from amazon
74
+ ec2.delete_key_pair("tunl_#{name}")
75
+ # delete local file
76
+ filepath = key_pair_filepath(name)
77
+ if File.exists?(filepath)
78
+ FileUtils.rm_f(filepath)
79
+ end
80
+ end
81
+
82
+ def setup_key_pair(name)
83
+ if ! File.exists?(key_pair_filepath(name))
84
+ create_key_pair(name)
85
+ end
86
+ end
87
+
88
+ def create_security_group(name)
89
+ log "create", "security group tunl_#{name}"
90
+ ec2.create_security_group("tunl_#{name}", 'created by tunl gem')
91
+ rescue RightAws::AwsError => e
92
+ if e.message =~ /already exists/
93
+ return true
94
+ else
95
+ raise e
96
+ end
97
+ end
98
+
99
+ def authorize_security_group(name)
100
+ [ 22, 80 ].each do |port|
101
+ begin
102
+ ec2.authorize_security_group_IP_ingress("tunl_#{name}", port, port)
103
+ rescue RightAws::AwsError => e
104
+ if e.message =~ /already been authorized/
105
+ # do nothing
106
+ else
107
+ raise e
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ def delete_security_group(name)
114
+ log "delete", "security group tunl_#{name}"
115
+ ec2.delete_security_group("tunl_#{name}")
116
+ end
117
+
118
+ def find_security_group(name)
119
+ ec2.describe_security_groups.find { |item| item[:aws_group_name]=="tunl_#{name}" }
120
+ end
121
+
122
+ def setup_security_group(name)
123
+ # returns the names of all existing security groups prefixed with tunl_
124
+ group = find_security_group(name)
125
+ if group.nil?
126
+ create_security_group(name)
127
+ authorize_security_group(name)
128
+ end
129
+ end
130
+
131
+ def find_instance(name)
132
+ ec2.describe_instances.find { |item| item[:aws_groups].include?("tunl_#{name}") and (item[:aws_state].include?("pending") or item[:aws_state].include?("running")) }
133
+ end
134
+
135
+ def create_instance(name)
136
+ # launch instance with:
137
+ # os - ubuntu hardy 32 bit
138
+ # security_group: tunl_name
139
+ # key_pair: tunl_name
140
+ log "launch", "instance ..."
141
+ instances = ec2.launch_instances('ami-ed46a784',
142
+ :key_name => "tunl_#{name}",
143
+ :group_ids => "tunl_#{name}",
144
+ :instance_type => 'm1.small',
145
+ :availability_zone => 'us-east-1a',
146
+ :user_data => %Q{#!/bin/sh
147
+ echo "starting" > /tmp/user_data_status.txt
148
+ echo "GatewayPorts clientspecified" >> /etc/ssh/sshd_config
149
+ /etc/init.d/ssh restart
150
+ echo "finished" > /tmp/user_data_status.txt
151
+ })
152
+ log "", "#{instances.first[:aws_instance_id]}"
153
+ end
154
+
155
+ def wait_for_instance(name)
156
+ log "", "waiting for dns ..."
157
+ while true
158
+ break if ( find_instance(name) and find_instance(name)[:dns_name].size>0 )
159
+ sleep(2)
160
+ end
161
+ log "", "done: #{find_instance(name)[:dns_name]}"
162
+ require 'socket'
163
+ dns_name = find_instance(name)[:dns_name]
164
+ log "", "waiting for ssh"
165
+ loop do
166
+ begin
167
+ Timeout::timeout(4) do
168
+ TCPSocket.new(dns_name, 22)
169
+ log "", "done!"
170
+ log ""
171
+ return
172
+ end
173
+ rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
174
+ sleep(2)
175
+ end
176
+ end
177
+ end
178
+
179
+ def delete_instance(name)
180
+ instance = find_instance(name)
181
+ log "delete", "instance tunl_#{name} with id #{instance[:aws_instance_id]}"
182
+ ec2.terminate_instances(instance[:aws_instance_id])
183
+ end
184
+
185
+ def setup_instance(name)
186
+ instance = find_instance(name)
187
+ if instance.nil?
188
+ create_instance(name)
189
+ else
190
+ log "connect", "instance #{instance[:aws_instance_id]}"
191
+ end
192
+ wait_for_instance(name)
193
+ end
194
+
195
+ def find_all_instances
196
+ ec2.describe_instances.find_all { |item| item[:aws_groups].first.include?('tunl_') and ['running', 'pending'].include?(item[:aws_state]) }
197
+ end
198
+
199
+ def log(action,message='')
200
+ $stdout.printf("%-3s %-8s %-12s\n", '', action, message)
201
+ end
202
+
203
+ end
204
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tunl
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - David Crockett
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2009-09-30 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: EC2 Proxy Service
22
+ email: davy@davcro.com
23
+ executables:
24
+ - tunl
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/tunl.rb
31
+ - bin/tunl
32
+ has_rdoc: true
33
+ homepage:
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.6
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: EC2 Proxy Service
62
+ test_files: []
63
+