Skip to content
This repository has been archived by the owner on May 1, 2022. It is now read-only.

Added Lettuce Tests for Docker Container Daemon #216

Open
wants to merge 2 commits into
base: a0xnirudh/docker
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions ContainerManager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,76 @@ Connecting to a Container Daemon:
2. Connect to socket connection running on port 5506 using `nc localhost 5506`.
3. Run the challenge by sending `create_connection:<challengename>` to the socket connection. This will return the web address of running challenge.

Running Unit Tests using lettuce:
-----------------

1. Install lettuce with pip by running: pip install lettuce
2. Change directory to ContainerManager
3. If running for the first time run install.py to download, install, build and configure the hackademic docker image.
4. Start the container-daemon.py in the background (./container-daemon.py > /dev/null &)
OR Run it on another terminal (Recommended for debugging)
5. Change directory to ContainerManager/tests
6. Run 'lettuce' (without quotes)
7. let's lettuce it!

Using the socket API with communicator.py
-----------------

The provided communicator.py script supports 3 commands:
1. create_container <Challenge Name>
2. kill_container <Container ID>
3. list_containers

Example Usage:
./communicator.py create_container samplechallenge
OUTPUT:
samplechallenge
http://localhost:4776/samplechallenge

./communicator.py list_containers
OUTPUT:
[{
"Status": "Up 15 seconds",
"Created": 1457876140,
"Image": "hackademic:latest",
"Labels": {},
"NetworkSettings": {
"Networks": {
"bridge": {
"NetworkID": "",
"MacAddress": "02:42:ac:11:00:02",
"GlobalIPv6PrefixLen": 0,
"Links": null,
"GlobalIPv6Address": "",
"IPv6Gateway": "",
"IPAMConfig": null,
"EndpointID": "5db72707f05bf3dfa4f67a9c803e126190293400d7d5f15a84f2e108bd733a4b",
"IPPrefixLen": 16,
"IPAddress": "172.17.0.2",
"Gateway": "172.17.0.1",
"Aliases": null
}
}
},
"HostConfig": {
"NetworkMode": "default"
},
"ImageID": "sha256:9e9357439b68e1d2f8230cd87b307560b1587fa2b36b2ce9649d3942e90416a6",
"Command": "/sbin/my_init",
"Names": ["/desperate_hamilton"],
"Id": "12a7b763a846fc05f1bfb41656ea1d045bfabf0788dc4b795bdf4f1e0c3a1329",
"Ports": [{
"IP": "0.0.0.0",
"Type": "tcp",
"PublicPort": 4776,
"PrivatePort": 80
}]
}]

./communicator.py kill_container 12a7b763a846fc05f1bfb41656ea1d045bfabf0788dc4b795bdf4f1e0c3a1329
OUTPUT:
12a7b763a846fc05f1bfb41656ea1d045bfabf0788dc4b795bdf4f1e0c3a1329



A UI has to be added to provide this as an interface to the admin.
1 change: 1 addition & 0 deletions ContainerManager/communicator.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import socket
import sys

__author__ = "AnirudhAnand (a0xnirudh) < [email protected]"

Expand Down
37 changes: 32 additions & 5 deletions ContainerManager/container_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def create_container(self, challenge=None):
str(exception))
exit("Check logs for more details")

print "[*] Container Created: " + cli.get("Id")
return "http://localhost:" + str(port) + "/" + str(challenge)

def generate_port(self):
Expand All @@ -57,16 +58,41 @@ def list_containers(self):

def kill_container(self, containerid):
self.client.kill(containerid)
print "[*] Container Killed: " + containerid

def auto_container_killer(self):
print "[*] Auto Container Killer Started"
# Store the network activity for all the containers
# Key: Container ID
# Value: Number of recieved packets (can be changed below, see 'rx_packets')
containers_network_activity = {}
while True:
sleep(300)
print "[*] Auto Container Killer Sleeping..."
# Poll every 10 minutes
sleep(600)
print "[*] Checking for Containers with no recieved packets since last check..."
container_list = self.client.containers()
for i in range(0, len(container_list)):
temp = container_list[i].get("Status")
flag = re.findall(self.timer, temp)
if flag:
self.kill_container(container_list[i].get("Id"))
container_id = container_list[i].get("Id")
stats = self.client.stats(container_id, True)
current_network_activity = 0

# 'stats' is a generator object
for stat in stats:
# Reading number of recieved packets in the docker container
current_network_activity = stat['networks']['eth0']['rx_packets']
break
try:
# If previous network activity is same as the current one then kill the container
if current_network_activity <= containers_network_activity[container_id]:
self.kill_container(container_id)
else:
# Update the network activity for the container in the dictionary
containers_network_activity[container_id] = current_network_activity
# If the container is newly created add an entry for it in the dictionary
except KeyError as err:
containers_network_activity[container_id] = current_network_activity


def create_socket(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Expand All @@ -78,6 +104,7 @@ def create_socket(self):
+ msg[1]
sys.exit(1)
s.listen(10)
print "[*] Docker Daemon started at: " + self.HOST + ":" + str(self.PORT)
while True:
conn, addr = s.accept()
thread.start_new_thread(self.client_thread, (conn,))
Expand Down
52 changes: 52 additions & 0 deletions ContainerManager/tests/features/steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from lettuce import *
from subprocess import check_output
import json

communicator_path = '../communicator.py'

@step("I create a docker container for '(.*)'")
def i_create_a_docker_container_for(step, challenge):
byte_output = check_output([communicator_path, 'create_container', challenge])

# Getting the port number for the newly created container
slash_split = byte_output.split('/')
colon_split = slash_split[2].split(':')
port_no = colon_split[1]
world.container_port = int(port_no)

@step("I see the container listed")
def i_see_the_container_listed(step):
byte_output = check_output([communicator_path, 'list_containers'])
containers = json.loads(byte_output)

# Getting the Container ID from the port number from previously created container
world.container_id = ''
for container in containers:
if container['Ports'][0]['PublicPort'] == world.container_port:
world.container_id = container['Id']
break

assert world.container_id != '', \
"Found Container %s" % world.container_id


@step("I kill the docker container")
def i_kill_the_docker_container(step):
check_output([communicator_path, 'kill_container', world.container_id])


@step("I do not see the container listed")
def i_do_not_see_it_listed(step):
byte_output = check_output([communicator_path, 'list_containers'])
containers = json.loads(byte_output)

# Checking whether the Container ID still exist in the list
containerFound = False
for container in containers:
if container['Id'] == world.container_id:
containerFound = True
break
assert not containerFound, "Found Container: %s" % str(containerFound)



14 changes: 14 additions & 0 deletions ContainerManager/tests/features/zero.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Feature: Manipulating Docker Containers
Creating/Killing/Listing a Docker Container using
the provided socket interface and
running 'samplechallenge'


Scenario: Create a container with samplechallenge
When I create a docker container for 'samplechallenge'
Then I see the container listed


Scenario: Killing the container
When I kill the docker container
Then I do not see the container listed