lxc-extra 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjA2YjFkNzA4ZTU0MzRjMjU3YjEyN2Q5ZmE4ZTYwMjAzMzNkOTMwNg==
4
+ ODA1YTcyNmFhODEwYmEyZTFkZDBiMjk1Y2ZkZTFiNWEwODg2ZmY0Yg==
5
5
  data.tar.gz: !binary |-
6
- YmYzYTI1YWQ2OGQ3YjRiMWNhYzlhMzEwMzQ2MmUxYzUxMDdmMjNmNw==
6
+ OTlkNTdiNTNlNTQ3YTAwZDExZTY0NWZmYjAxMDMyZjJiMWQ4OWE3Zg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MmVhMzg0NmFlNDBlNDQ4OTZlZmQ4ZTg3N2E2NTg1YjM4ZDY2MmYyMDI2NzIz
10
- YWUxODNhMGIwNDNkYzYwOWU1NGJmZjM0NTk1NmJiMDZlMjQ5MzIzMTYzNWQ2
11
- MGY0MmVkYjMwM2FlZjM1NDUwYzcyZjEyOTU0ZDNiZGMwYTk5NGM=
9
+ YjBmYTdkNDM2OTMyZThiNTczOTE4ZDBkMDFmNmNlYjNiMmNlYTdmZTFlYjZl
10
+ MDIzOGMzYTNiZTAzZDFlNzlhYmRhZDc1NDc4YjY1ZjI4Y2VkOGJhMDhmMDE2
11
+ OGEzNjZjN2MxZGQzN2E4YjI5MGQ1MTVjZDM5Y2M1MGYzNWRiYmE=
12
12
  data.tar.gz: !binary |-
13
- MDc0OTAwZGY4MmJkMWRiYTUxODU2NzFiMzFkNTBhZDYyOThjZTBjZGZlODAy
14
- NWM4ZjljMGEyMDFlZWQwYjQyOGFhNTlkZWE1MjBjZTdiZjE1NTYwZDMyYWYy
15
- ODZjNWQzNGFkOGNiZGI0YmVlN2U3MWE0YWIwZDU1MDRiZTU1OGM=
13
+ YjhiZjE1MTAyZmZiMWJhNjVlMGQ4ZTBmZWE5ZDJlNWYyNmNkNGRjYzJmODMz
14
+ ZGFkYjQwMTUxZGNhMmVmY2IwMzc5Y2RjNjc5OTgxMjI3Zjg4YzczNmE2Njlh
15
+ NTYxMjNjZWQwZTk1NDI3N2NmY2FiYzVmYjA5ODIxNWUwYjc4NjU=
@@ -1,24 +1,31 @@
1
1
  require 'lxc'
2
2
  require 'lxc/extra/version'
3
+ require 'lxc/extra/channel'
3
4
  require 'io/wait'
5
+ require 'timeout'
4
6
 
5
7
  module LXC
6
8
  module Extra
7
- def execute(&block)
9
+ def execute(opts= {}, &block)
10
+ attach_opts = opts[:attach_options] || {}
11
+ attach_opts[:wait] ||= true
12
+ timeout = opts[:timeout] || 3600
8
13
  r,w = IO.pipe
9
- ret = attach(wait:true) do
14
+ ret = attach(attach_opts) do
10
15
  ENV.clear
11
16
  ENV['PATH'] = '/usr/bin:/bin:/usr/sbin:/sbin'
12
17
  ENV['TERM'] = 'xterm-256color'
13
18
  ENV['SHELL'] = '/bin/bash'
14
19
  r.close
15
20
  begin
16
- out = block.call
17
- w.write(Marshal.dump(out))
21
+ Timeout::timeout(timeout) do
22
+ out = block.call
23
+ w.write(Marshal.dump(out))
24
+ end
18
25
  rescue Exception => e
19
26
  w.write(Marshal.dump(e))
20
27
  end
21
- end
28
+ end
22
29
  w.close
23
30
  o = nil
24
31
  if r.ready?
@@ -28,6 +35,14 @@ module LXC
28
35
  raise o if o.is_a?(Exception)
29
36
  o
30
37
  end
38
+
39
+ def open_channel(&block)
40
+ channel = ::LXC::Extra::Channel.new
41
+ pid = attach do
42
+ channel.listen(&block)
43
+ end
44
+ [channel, pid]
45
+ end
31
46
  end
32
47
  class Container
33
48
  include Extra
@@ -0,0 +1,169 @@
1
+ module LXC
2
+ module Extra
3
+ #
4
+ # Bidirectional Ruby communications between a host and an LXC container.
5
+ # It handles bidirectional communications: the host can send a message to
6
+ # the container using channel.send_message, and the container can send to
7
+ # the host using channel.send_message as well.
8
+ #
9
+ # This class is intended to be created before ct.attach and used by both
10
+ # the host and the container.
11
+ #
12
+ # == Shorthand Usage
13
+ #
14
+ # require 'lxc'
15
+ # require 'lxc/extra'
16
+ #
17
+ # container = LXC::Container.new('simple')
18
+ # channel, pid = container.open_channel do |host, number|
19
+ # puts "received #{number} INSIDE!"
20
+ # if number > 10
21
+ # host.stop
22
+ # else
23
+ # host.send_message(number+1)
24
+ # end
25
+ # end
26
+ # channel.send_message(1)
27
+ # channel.listen do |container, number|
28
+ # puts "received #{number} OUTSIDE!"
29
+ # container.send_message(number+1) unless number > 10
30
+ # end
31
+ #
32
+ # == Extended Usage
33
+ #
34
+ # channel = Channel.new
35
+ # ct.attach do
36
+ # channel.listen do |host, number|
37
+ # "received #{number} INSIDE!"
38
+ # if number > 10
39
+ # host.stop
40
+ # else
41
+ # host.send(number+1)
42
+ # end
43
+ # end
44
+ # end
45
+ # channel.listen do |container, number|
46
+ # "received #{number} OUTSIDE!"
47
+ # container.send(number+1)
48
+ # end
49
+ #
50
+ class Channel
51
+ #
52
+ # Create a new LXC channel.
53
+ #
54
+ def initialize
55
+ @from_container, @to_host = IO.pipe
56
+ @from_host, @to_container = IO.pipe
57
+ @host_pid = Process.pid
58
+ end
59
+
60
+ attr_reader :from_container
61
+ attr_reader :to_host
62
+ attr_reader :from_host
63
+ attr_reader :to_container
64
+ attr_reader :host_pid
65
+
66
+ #
67
+ # Listen for data from the other side. Loops until stop is received.
68
+ #
69
+ # == Arguments
70
+ # &block:: callback
71
+ #
72
+ # == Example
73
+ #
74
+ # channel.listen do |*args|
75
+ # puts "Received #{args} from the other side."
76
+ # end
77
+ #
78
+ def listen(&block)
79
+ while true
80
+ args = self.next
81
+ if args.is_a?(Stop)
82
+ return
83
+ end
84
+ block.call(self, *args)
85
+ end
86
+ end
87
+
88
+ #
89
+ # Send a message to the other side.
90
+ # You may send arbitrary Ruby, which will be sent with Marshal.dump.
91
+ #
92
+ # == Arguments
93
+ # args:: a list of arguments to send. Arbitrary Ruby objects may be sent.
94
+ # The other side's listen callback will receive these arguments.
95
+ #
96
+ def send_message(*args)
97
+ Marshal.dump(args, write_fd)
98
+ end
99
+
100
+ #
101
+ # Get the next result.
102
+ #
103
+ # == Arguments
104
+ # stop_result:: result to return when the channel wants to shut down. Defaults to LXC::Extra::Channel::Stop.singleton
105
+ #
106
+ # == Returns
107
+ # Returns nextLXC::Extra::Channel::Stop instance if the channel closes.
108
+ def next(stop_result = Stop.singleton)
109
+ if read_fd.closed?
110
+ stop_result
111
+ else
112
+ Marshal.load(read_fd)
113
+ end
114
+ end
115
+
116
+ #
117
+ # Stop the channel. Sends a message to the other side to stop it, too.
118
+ #
119
+ def stop
120
+ send_message(Stop.singleton)
121
+ @from_container.close
122
+ @to_host.close
123
+ @from_host.close
124
+ @to_container.close
125
+ end
126
+
127
+ #
128
+ # Pretend a message was sent from the other side.
129
+ #
130
+ # For debug purposes.
131
+ #
132
+ def pretend_message_was_sent(*args)
133
+ if Process.pid == @host_pid
134
+ Marshal.dump(args, @to_host)
135
+ else
136
+ Marshal.dump(args, @to_container)
137
+ end
138
+ end
139
+
140
+ #
141
+ # File descriptor that sends data to the other side.
142
+ #
143
+ def write_fd
144
+ if Process.pid == @host_pid
145
+ @to_container
146
+ else
147
+ @to_host
148
+ end
149
+ end
150
+
151
+ #
152
+ # File descriptor that receives data from the other side.
153
+ #
154
+ def read_fd
155
+ if Process.pid == @host_pid
156
+ @from_container
157
+ else
158
+ @from_host
159
+ end
160
+ end
161
+
162
+ class Stop
163
+ def self.singleton
164
+ @@singleton ||= Stop.new
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,127 @@
1
+ require 'lxc/extra/selector'
2
+
3
+ module LXC
4
+ module Extra
5
+ #
6
+ # Proxy server that listens for connections on the client side and sends
7
+ # the data to the corresponding ProxyServerSide through an Channel.
8
+ #
9
+ # == Usage
10
+ #
11
+ # # Forward 127.0.0.1:80 in the container to google.com from the host
12
+ # channel = LXC::Extra::Channel.new
13
+ # pid = container.attach do
14
+ # server = TCPServer.new('127.0.0.1', 5559)
15
+ # proxy = LXC::Extra::ProxyClientSide.new(channel, server)
16
+ # proxy.start
17
+ # end
18
+ #
19
+ # # Here is the proxy server
20
+ # proxy = LXC::Extra::ProxyServerSide.new(channel) do
21
+ # TCPSocket.new('127.0.0.1', 9995)
22
+ # end
23
+ # proxy.start
24
+ #
25
+ class ProxyClientSide
26
+ #
27
+ # Create a new ProxyClientSide.
28
+ #
29
+ # == Arguments
30
+ # channel:: Channel to communicate over
31
+ # listen_server:: TCPServer to listen to
32
+ #
33
+ def initialize(channel, listen_server)
34
+ @channel = channel
35
+ @listen_server = listen_server
36
+ @sockets = {}
37
+ end
38
+
39
+ attr_reader :channel
40
+ attr_reader :listen_server
41
+
42
+ #
43
+ # Start forwarding connections and data. This call will not return until
44
+ # stop is called.
45
+ #
46
+ def start
47
+ begin
48
+ begin
49
+ @selector = Selector.new
50
+ @selector.on_select(@listen_server, &method(:on_server_accept))
51
+ @selector.on_select(@channel.read_fd, &method(:on_channel_response))
52
+ @selector.main_loop
53
+ ensure
54
+ @sockets.values.each { |socket| socket.close }
55
+ @sockets = {}
56
+ @selector = nil
57
+ end
58
+ rescue
59
+ STDERR.puts("Proxy server error on client side: #{$!}\n#{$!.backtrace.join("\n")}")
60
+ @channel.send_message(:server_error, $!)
61
+ raise
62
+ end
63
+ end
64
+
65
+ #
66
+ # Stop forwarding connections and data. Existing connections will be closed
67
+ # and a stop message will be sent to the other side.
68
+ #
69
+ def stop
70
+ @sockets.values.each { |socket| socket.close }
71
+ @sockets = {}
72
+ @selector = nil
73
+ @channel.send_message(:stop)
74
+ end
75
+
76
+ private
77
+
78
+ def on_server_accept(server)
79
+ socket = server.accept
80
+ @selector.on_select(socket, &method(:on_client_data))
81
+ @sockets[socket.fileno] = socket
82
+ @channel.send_message(:open, socket.fileno)
83
+ end
84
+
85
+ def on_client_data(socket)
86
+ message, *args = socket.recvmsg
87
+ if message.length == 0
88
+ @channel.send_message(:close, socket.fileno)
89
+ @selector.delete(socket)
90
+ @sockets.delete(socket.fileno)
91
+ socket.close
92
+ else
93
+ @channel.send_message(:data, socket.fileno, message)
94
+ end
95
+ end
96
+
97
+ def on_channel_response(read_fd)
98
+ message_type, fileno, *args = @channel.next(:stop)
99
+ begin
100
+ case message_type
101
+ when :data
102
+ @sockets[fileno].sendmsg(args[0])
103
+ when :close, :connection_error
104
+ @sockets[fileno].close
105
+ @selector.delete(@sockets[fileno])
106
+ @sockets.delete(fileno)
107
+ when :stop, :server_error
108
+ return :stop
109
+ else
110
+ raise "Unknown message type #{message_type} passed from server to client side proxy"
111
+ end
112
+ rescue
113
+ STDERR.puts("Error in on_channel_response: #{$!}\n#{$!.backtrace.join("\n")}")
114
+ if fileno
115
+ @server.send_message(:connection_error, fileno, $!) unless message_type == :close || message_type == :connection_error
116
+ socket = @sockets[fileno]
117
+ if socket
118
+ socket.close
119
+ @selector.delete(socket)
120
+ end
121
+ @sockets.delete(fileno)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,122 @@
1
+ require 'lxc/extra/selector'
2
+
3
+ module LXC
4
+ module Extra
5
+ #
6
+ # Proxy server that listens for connections froom the corresponsing
7
+ # ProxyClientSide and sends them to the actual server.
8
+ #
9
+ # == Usage
10
+ #
11
+ # # Forward 127.0.0.1:80 in the container to google.com from the host
12
+ # channel = LXC::Extra::Channel.new
13
+ # pid = container.attach do
14
+ # server = TCPServer.new('127.0.0.1', 5559)
15
+ # proxy = LXC::Extra::ProxyClientSide.new(channel, server)
16
+ # proxy.start
17
+ # end
18
+ #
19
+ # # Here is the proxy server
20
+ # proxy = LXC::Extra::ProxyServerSide.new(channel) do
21
+ # TCPSocket.new('127.0.0.1', 9995)
22
+ # end
23
+ # proxy.start
24
+ #
25
+ class ProxyServerSide
26
+ #
27
+ # Create a new ProxyServerSide.
28
+ #
29
+ # == Arguments
30
+ # channel: Channel to send/receive connections from the other side.
31
+ # &server_connector:: block that will be called (with no arguments) when the server
32
+ # opens a new connection, to create a new connection to the
33
+ # server. Must return a socket.
34
+ #
35
+ def initialize(channel, &server_connector)
36
+ @channel = channel
37
+ @server_connector = server_connector
38
+ end
39
+
40
+ attr_reader :channel
41
+ attr_reader :server_connector
42
+
43
+ #
44
+ # Start forwarding connections from the other side to the server. Blocks
45
+ # until stop is called.
46
+ #
47
+ def start
48
+ @filenos = {}
49
+ @sockets = {}
50
+ begin
51
+ @selector = Selector.new
52
+ @selector.on_select(channel.read_fd, &method(:on_channel_message))
53
+ @selector.main_loop
54
+ ensure
55
+ @sockets.values.each { |socket| socket.close }
56
+ end
57
+ end
58
+
59
+ #
60
+ # Stop forwarding connections and data. Existing connections will be closed
61
+ # and a stop message will be sent to the other side.
62
+ #
63
+ def stop
64
+ @selector.stop
65
+ end
66
+
67
+ private
68
+
69
+ def on_channel_message(read_fd)
70
+ message_type, fileno, *args = @channel.next(:stop)
71
+ socket = @sockets[fileno]
72
+ begin
73
+ case message_type
74
+ when :open
75
+ socket = @server_connector.call
76
+ @selector.on_select(socket, &method(:on_server_response))
77
+ @sockets[fileno] = socket
78
+ @filenos[socket] = fileno
79
+ when :data
80
+ socket.sendmsg(args[0])
81
+ when :close, :connection_error
82
+ socket.close
83
+ @selector.delete(socket)
84
+ @filenos.delete(socket)
85
+ @sockets.delete(fileno)
86
+ when :stop, :server_error
87
+ return :stop
88
+ else
89
+ raise "Unknown message type #{message_type} passed from channel to host"
90
+ end
91
+ rescue
92
+ STDERR.puts("Error in on_channel_message: #{$!}\n#{$!.backtrace.join("\n")}")
93
+ if fileno
94
+ @channel.send_message(:connection_error, fileno) unless message_type == :close || message_type == :connection_error
95
+ if socket
96
+ socket.close
97
+ @selector.delete(socket)
98
+ @filenos.delete(socket)
99
+ end
100
+ @sockets.delete(fileno)
101
+ end
102
+ end
103
+ end
104
+
105
+ def on_server_response(socket)
106
+ message, *args = socket.recvmsg
107
+ fileno = @filenos[socket]
108
+ if message.length == 0
109
+ socket.close
110
+ if fileno
111
+ @channel.send_message(:close, fileno)
112
+ @sockets.delete(fileno)
113
+ end
114
+ @filenos.delete(socket)
115
+ @selector.delete(socket)
116
+ else
117
+ @channel.send_message(:data, fileno, message)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,103 @@
1
+ module LXC
2
+ module Extra
3
+ #
4
+ # Provides a way to listen to multiple disparate file descriptors with
5
+ # different callbacks to handle each one.
6
+ #
7
+ # == Example
8
+ #
9
+ # require 'lxc/extra/selector'
10
+ # require 'socket'
11
+ #
12
+ # def repeat_to(socket, selector, out)
13
+ # message, *other = socket.recvmsg
14
+ # if message.size == 0
15
+ # selector.delete(socket)
16
+ # return
17
+ # end
18
+ # out.write(message)
19
+ # end
20
+ #
21
+ # selector = LXC::Extra::Selector.new()
22
+ #
23
+ # # Listen on 20480 and echo output to STDOUT
24
+ # server = TCPServer.new(20480)
25
+ # selector.on_select(server) do |server|
26
+ # socket = server.accept
27
+ # selector.on_select(socket, selector, STDOUT, &method(:repeat_to))
28
+ # end
29
+ #
30
+ # # Listen on 20481 and echo output to STDERR
31
+ # server = TCPServer.new(2049)
32
+ # selector.on_select(server) do |server|
33
+ # socket = server.accept
34
+ # selector.on_select(socket, selector, STDERR, &method(:repeat_to))
35
+ # end
36
+ #
37
+ # # Do all that listening on one thread!
38
+ # selector.main_loop
39
+ #
40
+ # NOTE: This is not presently thread-safe in that adds and deletes must be
41
+ # done on the same thread as the main_loop.
42
+ #
43
+ class Selector
44
+ #
45
+ # Create a new selector.
46
+ #
47
+ def initialize
48
+ @fd_callbacks = {}
49
+ end
50
+
51
+ #
52
+ # Add a file descriptor to listen on.
53
+ #
54
+ # == Arguments
55
+ #
56
+ # fd:: file descriptor to listen on.
57
+ # args:: optional set of arguments to pass to callback
58
+ # callback:: callback to call when file descriptor has data available
59
+ #
60
+ def on_select(fd, *args, &callback)
61
+ @fd_callbacks[fd] = {
62
+ :args => args,
63
+ :callback => callback
64
+ }
65
+ end
66
+
67
+ #
68
+ # Stop listening to a file descriptor.
69
+ #
70
+ # == Arguments
71
+ #
72
+ # fd:: file descriptor to stop listening to.
73
+ #
74
+ def delete(fd)
75
+ @fd_callbacks.delete(fd)
76
+ end
77
+
78
+ #
79
+ # Listen for any of the given file handles to be ready (with IO.select)
80
+ # and call their callbacks when they are.
81
+ #
82
+ def main_loop
83
+ begin
84
+ while true
85
+ ready = IO.select(@fd_callbacks.keys)
86
+ ready[0].each do |fd|
87
+ callback = @fd_callbacks[fd]
88
+ if callback
89
+ args = [ fd ]
90
+ args += callback[:args] if callback[:args]
91
+ if callback[:callback].call(*args) == :stop
92
+ return
93
+ end
94
+ end
95
+ end
96
+ end
97
+ rescue
98
+ raise
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,5 +1,5 @@
1
1
  module LXC
2
2
  module Extra
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'lxc/extra/channel'
3
+
4
+ describe LXC::Extra::Channel do
5
+
6
+ before(:all) do
7
+ c = LXC::Container.new('test')
8
+ c.create('ubuntu') unless c.defined?
9
+ c.start unless c.running?
10
+ end
11
+
12
+ it 'an open channel should successfully pass multiple objects' do
13
+ c = LXC::Container.new('test')
14
+ channel = LXC::Extra::Channel.new
15
+ pid = c.attach do
16
+ begin
17
+ channel.listen do |host, number|
18
+ host.send_message(number+1)
19
+ end
20
+ end
21
+ end
22
+ begin
23
+ results = []
24
+ channel.send_message(1)
25
+ channel.listen do |container, number|
26
+ results << number
27
+ if number > 5
28
+ container.stop
29
+ else
30
+ container.send_message(number)
31
+ end
32
+ end
33
+ Process.waitpid(pid)
34
+ rescue
35
+ Process.kill('KILL', pid)
36
+ raise
37
+ end
38
+ expect(results).to eq([ 2, 3, 4, 5, 6 ])
39
+ end
40
+
41
+ it 'container.open_channel works the same as the explicit version' do
42
+ c = LXC::Container.new('test')
43
+ channel, pid = c.open_channel do |host, number|
44
+ host.send_message(number+1)
45
+ end
46
+ begin
47
+ results = []
48
+ channel.send_message(1)
49
+ channel.listen do |container, number|
50
+ results << number
51
+ if number > 5
52
+ container.stop
53
+ else
54
+ container.send_message(number)
55
+ end
56
+ end
57
+ Process.waitpid(pid)
58
+ rescue
59
+ Process.kill('KILL', pid)
60
+ raise
61
+ end
62
+ expect(results).to eq([ 2, 3, 4, 5, 6 ])
63
+ end
64
+ end
@@ -29,5 +29,14 @@ describe LXC::Extra do
29
29
  ct.execute {raise TestError}
30
30
  end.to raise_error(TestError)
31
31
  end
32
+ it 'should respect timeout' do
33
+ expect do
34
+ ct.execute(timeout: 10) do
35
+ 11.times do |n|
36
+ sleep 1
37
+ end
38
+ end
39
+ end.to raise_error Timeout::Error
40
+ end
32
41
  end
33
42
  end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+ require 'lxc/extra/channel'
3
+ require 'lxc/extra/proxy_client_side'
4
+ require 'lxc/extra/proxy_server_side'
5
+ require 'lxc/extra/selector'
6
+ require 'socket'
7
+
8
+ describe 'LXC proxy' do
9
+
10
+ before(:all) do
11
+ c = LXC::Container.new('test')
12
+ c.create('ubuntu') unless c.defined?
13
+ c.start unless c.running?
14
+ end
15
+
16
+ def rescue_me(&block)
17
+ begin
18
+ block.call
19
+ rescue
20
+ STDERR.puts "ERROR in thread: #{$!}"
21
+ STDERR.puts $!.backtrace.join("\n")
22
+ raise
23
+ end
24
+ end
25
+
26
+ def safe_thread(&block)
27
+ Thread.new do
28
+ rescue_me(&block)
29
+ end
30
+ end
31
+
32
+ it 'proxy server receives multiple connections and passes data back and forth' do
33
+ container = LXC::Container.new('test')
34
+
35
+ threads = []
36
+ pids = []
37
+ begin
38
+ # Forward 127.0.0.1:80 in the container to google.com from the host
39
+ channel = LXC::Extra::Channel.new
40
+ pids << container.attach do
41
+ rescue_me do
42
+ server = TCPServer.new('127.0.0.1', 5559)
43
+ proxy = LXC::Extra::ProxyClientSide.new(channel, server)
44
+ proxy.start
45
+ end
46
+ end
47
+
48
+ # Here is the proxy server
49
+ threads << safe_thread do
50
+ proxy = LXC::Extra::ProxyServerSide.new(channel) do
51
+ TCPSocket.new('127.0.0.1', 9995)
52
+ end
53
+ proxy.start
54
+ end
55
+
56
+ # Here is the real TCP server (listener)
57
+ real_server = TCPServer.new('127.0.0.1', 9995)
58
+ threads << safe_thread do
59
+ selector = LXC::Extra::Selector.new
60
+ selector.on_select(real_server) do |server|
61
+ socket = server.accept
62
+ selector.on_select(socket) do |socket|
63
+ message, *other = socket.recvmsg
64
+ if message.size == 0
65
+ selector.delete(socket)
66
+ else
67
+ socket.sendmsg((message.to_i*2).to_s)
68
+ end
69
+ end
70
+ end
71
+ selector.main_loop
72
+ end
73
+
74
+ channel2 = LXC::Extra::Channel.new
75
+ # Let's open up a client in the container!
76
+ container.execute do
77
+ rescue_me do
78
+ socket = TCPSocket.new('127.0.0.1', 5559)
79
+ socket.sendmsg('10')
80
+ response, *other = socket.recvmsg
81
+ channel2.send_message(response)
82
+ socket.sendmsg('30')
83
+ response, *other = socket.recvmsg
84
+ channel2.send_message(response)
85
+ socket.close
86
+ end
87
+ end
88
+ container.execute do
89
+ rescue_me do
90
+ socket = TCPSocket.new('127.0.0.1', 5559)
91
+ socket.sendmsg('50')
92
+ response, *other = socket.recvmsg
93
+ channel2.send_message(response)
94
+ socket.sendmsg('70')
95
+ response, *other = socket.recvmsg
96
+ channel2.send_message(response)
97
+ socket.close
98
+ end
99
+ end
100
+ expect(channel2.next).to eq(['20'])
101
+ expect(channel2.next).to eq(['60'])
102
+ expect(channel2.next).to eq(['100'])
103
+ expect(channel2.next).to eq(['140'])
104
+ ensure
105
+ threads.each do |t|
106
+ t.kill
107
+ end
108
+ pids.each do |pid|
109
+
110
+ Process.kill(9, pid)
111
+ end
112
+ end
113
+ end
114
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lxc-extra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ranjib Dey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-08 00:00:00.000000000 Z
11
+ date: 2014-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-lxc
@@ -79,10 +79,16 @@ files:
79
79
  - README.md
80
80
  - Rakefile
81
81
  - lib/lxc/extra.rb
82
+ - lib/lxc/extra/channel.rb
83
+ - lib/lxc/extra/proxy_client_side.rb
84
+ - lib/lxc/extra/proxy_server_side.rb
85
+ - lib/lxc/extra/selector.rb
82
86
  - lib/lxc/extra/version.rb
83
87
  - lxc-extra.gemspec
84
88
  - spec/spec_helper.rb
89
+ - spec/unit/lxc_channel_spec.rb
85
90
  - spec/unit/lxc_extra_spec.rb
91
+ - spec/unit/lxc_proxy_spec.rb
86
92
  homepage: https://github.com/ranjib/lxc-extra
87
93
  licenses:
88
94
  - MIT
@@ -109,4 +115,6 @@ specification_version: 4
109
115
  summary: Additional helper methods for ruby lxc binding
110
116
  test_files:
111
117
  - spec/spec_helper.rb
118
+ - spec/unit/lxc_channel_spec.rb
112
119
  - spec/unit/lxc_extra_spec.rb
120
+ - spec/unit/lxc_proxy_spec.rb