lxc-extra 0.0.2 → 0.0.3

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.
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