def test_ip_diversity(self): try: sc = Container() sc2 = Container() sc.start(DATA) sc2.start(DATA) sc.set_capabilities(["test.cap.policy-accept-ping"]) sc2.set_capabilities(["test.cap.policy-accept-ping"]) sc.launch_command("python " + sc.get_bind_dir() + "/testhelper.py " + " --test-dir " + sc.get_bind_dir() + " --do-ifconfig f1") time.sleep(1) sc2.launch_command("python " + sc.get_bind_dir() + "/testhelper.py " + " --test-dir " + sc.get_bind_dir() + " --do-ifconfig f2") time.sleep(1) helper = NetworkHelper(CURRENT_DIR) assert 0 == helper.compare_ips("f1", "f2") helper.remove_files("f1", "f2") finally: sc.terminate()
def test_caps_for_one_container_does_not_affect_another_container(self): """ Test that setting caps for one containers does not have an impact on another container. Test steps: * Create two containers, sc1 and sc2 * Set cap A and B on sc1 * Assert the two expected variables are set, one from cap A and one from B * Set cap A on sc2 * Assert ony the one expected variable is set, the one from cap A, and that the one from cap B is not set. """ sc1 = Container() sc2 = Container() try: sc1.start(DATA) sc2.start(DATA) # These caps should set two different env vars in the container sc1.set_capabilities(["environment.test.cap.7", "environment.test.cap.8"]) sc1.launch_command("python " + sc1.get_bind_dir() + "/testhelper.py --test-dir " + sc1.get_bind_dir() + "/testoutput" + " --do-get-env-vars") # The D-Bus LaunchCommand is asynch so let it take effect before assert time.sleep(0.5) helper = EnvironmentHelper(TESTOUTPUT_DIR) my_env_var_1 = helper.env_var("MY_ENV_VAR_1") my_env_var_2 = helper.env_var("MY_ENV_VAR_2") assert my_env_var_1 == "1" and my_env_var_2 == "2" # This cap should set only one env var in the container sc2.set_capabilities(["environment.test.cap.7"]) sc2.launch_command("python " + sc2.get_bind_dir() + "/testhelper.py --test-dir " + sc2.get_bind_dir() + "/testoutput" + " --do-get-env-vars") # The D-Bus LaunchCommand is asynch so let it take effect before assert time.sleep(0.5) my_env_var_1 = helper.env_var("MY_ENV_VAR_1") my_env_var_2 = helper.env_var("MY_ENV_VAR_2") assert my_env_var_1 == "1" and my_env_var_2 == None finally: sc1.terminate() sc2.terminate()
def test_memory_cgroup_limit(self): """ Test if the memory limiting with cgroup gateway is working as expected which behavior is when allocated more memory than limit it should fail """ try: sc = Container() sc.start(DATA) sc.set_capabilities(["test.cap.memory.share"]) memory_limitation = 1024 * 1024 * 1024 sc.launch_command("python " + sc.get_bind_dir() + "/testhelper.py" + " --test-dir " + sc.get_bind_dir() + " --do-allocate " + str(memory_limitation)) # wait 3 seconds for previous operation to end time.sleep(3) helper = CGroupHelper(CURRENT_DIR) allocation_return = helper.result() assert allocation_return < memory_limitation helper.remove_file() finally: sc.terminate()
def test_client_inside_reaches_outside_service(self): """ Test that an app running inside a container can call allowed methods on service outside of the container. """ c = Container() service = subprocess.Popen([ "python3", CURRENT_DIR + "/dbusapp.py", "server", "--outdir", TESTOUTPUT_DIR ], env=environ.copy(), stdout=subprocess.PIPE) try: c.start(DATA) c.set_capabilities(["test.cap.gwconfig"]) c.launch_command('{}/dbusapp.py client --method Ping'.format( c.get_bind_dir())) time.sleep(0.5) with open(TESTOUTPUT_DIR + "/service_output", "r") as fh: content = fh.read() assert "Hello" in content finally: c.terminate() service.terminate()
def test_old_containers_cleanup(self): """ The first goal of this test is to create a sitation where files from a previous run of SC are left on the file system. To be able to produce it a SC is started and after that softwarecontainer-agent is killed. The second phase of the test is to run SC again and make sure it does not fail because of any files left from the previous run. """ sc = Container() container_id = sc.start(DATA) os.system("pkill --signal SIGKILL -f softwarecontainer-agent") time.sleep(0.5) os.system("lxc-ls >> output") with open("output", "r") as fh: file_content = fh.readline() file_content = file_content.strip() assert file_content == ("SC-" + str(container_id)) os.remove("output") agent_handler = SoftwareContainerAgentHandler(logfile_path(), None, None) time.sleep(0.5) os.system("lxc-ls >> output") with open("output", "r") as fh: file_content = fh.readline() assert file_content == "" agent_handler.terminate() os.remove("output")
def test_core_is_dumped(self, core_pattern): """ Test that the core is dumped properly """ sc = Container() try: sc.start(DATA) # Start the helper that creates a core dump # This helper will call os.abort() on itself, which should generate a core dump pid = sc.launch_command("python " + sc.get_bind_dir() + "/testhelper.py --test-dir " + sc.get_bind_dir() + "/testoutput" + " --core-dump " + coredump_path()) # Let the helper run a little time.sleep(0.1) # Check that a core was dumped, meaning there are more files in the core dump dir now coreFiles = os.listdir(coredump_path()) assert (len(coreFiles) == 1) # The core file should not have size 0 fileStat = os.stat(coredump_path() + "/" + coreFiles[0]) assert (fileStat.st_size > 0) # The values that the helper found inside the container are verified helper = CoreDumpHelper(TESTOUTPUT_DIR) assert (helper.patternResult().strip() == core_pattern) assert (helper.limitResult() != 0) finally: sc.terminate()
def test_suspend_halts_execution_and_resumes_on_resume(self): """ Test that a running application inside the container is frozen when the container is suspended, and that it is resuming execution when the container is resumed. """ filename = "yes_output.log" try: sc = Container() sc.start(DATA) sc.launch_command("yes", stdout=filename) assert isFileGrowing(filename) # Suspend the container sc.suspend() assert not isFileGrowing(filename) sc.resume() assert isFileGrowing(filename) finally: sc.terminate() os.remove(filename)
def test_env_var_is_prepended(self): """ Set a config and then an prepending config, assert the prepended variable looks as expected. """ sc = Container() try: sc.start(DATA) sc.set_capabilities(["environment.test.cap.5"]) sc.launch_command("python " + sc.get_bind_dir() + "/testhelper.py --test-dir " + sc.get_bind_dir() + "/testoutput" + " --do-get-env-vars") # The D-Bus LaunchCommand is asynch so let it take effect before assert time.sleep(0.5) helper = EnvironmentHelper(TESTOUTPUT_DIR) ld_library_path = helper.env_var("LD_LIBRARY_PATH") # Assert the append was applied assert ld_library_path == "/another/path:/some/non/relevant/path" my_env_var = helper.env_var("MY_ENV_VAR") # Assert other variables were not affected assert my_env_var == "1234" finally: sc.terminate()
def test_spam_out(self): """ Launch client in container and stress test the communication out """ ca = Container() try: serv = dbusapp.Server(TESTOUTPUT_DIR) serv.start() ca.start(DATA) ca.set_capabilities(["test.cap.gwconfig"]) clients = 100 message_size = 8192 # Bytes t0 = time.time() for x in range(0, clients): ca.launch_command('{}/dbusapp.py client --size {}'.format( ca.get_bind_dir(), message_size)) t1 = time.time() assert serv.wait_until_requests(multiplier=clients) is True t2 = time.time() print("\n") print("Clients started: {0:.4f} seconds\n".format(t1 - t0)) print("Server received all messages: {0:.4f} seconds\n".format(t2 - t1)) print("Total time: {0:.4f} seconds\n".format(t2 - t0)) finally: ca.terminate() serv.terminate() serv = None
def run_test(num_starts=3): apps = list() for app in range(0, num_starts): """ Start numStarts apps in softwarecontainer. """ print "Start app " + str(app) container = Container() # A basic container configuration, content is not important for this test. container_data = { Container.CONFIG: '[{"writeBufferEnabled": false}]', Container.BIND_MOUNT_DIR: "/gateways/app", Container.HOST_PATH: CURRENT_DIR, Container.READONLY: False } container.start(container_data) # A minimal gateway config so the gateway can be configured and enabled. container.set_capabilities(["test.cap.profiling"]) container.launch_command("/gateways/app/simple") apps.append(container) time.sleep(5) # Tear down all started apps for app in apps: app.terminate()
def test_allow_ping_only_for_specific_domain(self): """ Tests the specific domain is allowed to ping. And other domains are not allowed to ping. """ try: sc = Container() sc.start(DATA) sc.set_capabilities(["test.cap.accept-example-com"]) sc.launch_command("python " + sc.get_bind_dir() + "/testhelper.py" + " --test-dir " + sc.get_bind_dir() + " --do-ping IANA.com") # Allow 'ping' to timeout (the currently used busybox ping can't have custom timeout) time.sleep(10) helper = NetworkHelper(CURRENT_DIR) is_pingable = helper.ping_result() assert is_pingable is False helper.remove_file() sc.launch_command("python " + sc.get_bind_dir() + "/testhelper.py" + " --test-dir " + sc.get_bind_dir() + " --do-ping example.com") time.sleep(2) is_pingable = helper.ping_result() assert is_pingable is True helper.remove_file() finally: sc.terminate()
def test_bindmount_sockets(self, flag): """ Test that sockets are bindmountable as expected. """ absoluteTestFile = os.path.join("/tmp/", TESTFILE) if not os.path.exists(absoluteTestFile): server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) server.bind(absoluteTestFile) ca = Container() if flag is True: DATA[Container.CONFIG] = '[{"enableWriteBuffer": true}]' else: DATA[Container.CONFIG] = '[{"enableWriteBuffer": false}]' try: ca.start(DATA) ca.bindmount(absoluteTestFile, "/gateways/testfile", True) # Give the command time to run inside the container time.sleep(0.5) assert os.path.exists(absoluteTestFile) is True ca.launch_command('{}/fileapp.py check {}'.format( ca.get_bind_dir(), "/gateways/testfile")) time.sleep(0.5) assert os.path.exists(absoluteTestFile) is True finally: ca.terminate() server.close() if os.path.exists(absoluteTestFile): os.remove(absoluteTestFile)
def test_vars_set_by_api_takes_precedence(self): """ Setting a varaible through the D-Bus API should mean that value takes precedence over a variable previously set by the environment gateway. """ sc = Container() try: sc.start(DATA) # Set variable through gateway sc.set_capabilities(["environment.test.cap.4"]) # Set the same variable with LaunchCommand env_var = {"MY_ENV_VAR": "new-value"} sc.launch_command( "python " + sc.get_bind_dir() + "/testhelper.py --test-dir " + sc.get_bind_dir() + "/testoutput" + " --do-get-env-vars", env=env_var) # The D-Bus LaunchCommand is asynch so let it take effect before assert time.sleep(0.5) helper = EnvironmentHelper(TESTOUTPUT_DIR) my_env_var = helper.env_var("MY_ENV_VAR") # Assert value is the one set with LaunchCommand assert my_env_var == "new-value" finally: sc.terminate()
def test_bindmount_files(self, flag): """ Test that files are bindmountable as expected. """ absoluteTestFile = os.path.join(CURRENT_DIR, TESTFILE) if not os.path.exists(absoluteTestFile): f = open(absoluteTestFile, "w") f.write("gobbles") f.close() ca = Container() if flag is True: DATA[Container.CONFIG] = '[{"enableWriteBuffer": true}]' else: DATA[Container.CONFIG] = '[{"enableWriteBuffer": false}]' try: ca.start(DATA) ca.bindmount(absoluteTestFile, "/gateways/testfile", True) # Give the command time to run inside the container time.sleep(0.5) assert os.path.exists(absoluteTestFile) is True ca.launch_command('{}/fileapp.py check {}'.format( ca.get_bind_dir(), "/gateways/testfile")) time.sleep(0.5) assert os.path.exists(absoluteTestFile) is True finally: ca.terminate() if os.path.exists(absoluteTestFile): os.remove(absoluteTestFile)
def test_memory_cgroup_whitelisting(self): """ Test if the whitelisting on cgroup memory is working as expected which behavior is more permissive configuration should be applied on memory.limit_in_bytes """ try: sc = Container() cid = sc.start(DATA) containerID = "SC-" + str(cid) sc.set_capabilities(["test.cap.memory.whitelist"]) most_permissive_value = 1024 * 1024 time.sleep(0.5) with open( "/sys/fs/cgroup/memory/lxc/" + containerID + "/memory.limit_in_bytes", "r") as fh: limit_in_bytes = int(fh.read()) assert limit_in_bytes == most_permissive_value most_permissive_value = 10 * 1024 * 1024 with open( "/sys/fs/cgroup/memory/lxc/" + containerID + "/memory.memsw.limit_in_bytes", "r") as fh: memsw_limit = int(fh.read()) assert memsw_limit == most_permissive_value finally: sc.terminate()
def test_tmpfs_writebuffer_size_disabled(self): """ The SoftwareContainer will create a tmpfs in the location of the containers temporary directory location if the write buffer is enabled. Test that the tmpfs mounted in the containers temporary directory structure works as expected when the writebuffer is disabled. """ absoluteTestFile = None ca = Container() DATA[Container.CONFIG] = '[{"writeBufferEnabled": false}]' try: id = ca.start(DATA) absoluteTestFile = os.path.join("/tmp/container/SC-" + str(id) + "/", TESTFILE) with open(absoluteTestFile, "w") as f: f.write("w" * 105 * 1024 * 1024) finally: ca.terminate() if os.path.exists(absoluteTestFile): os.remove(absoluteTestFile)
def test_list_containers(self): """ Test listing available containers works as expected """ sc1 = Container() sc2 = Container() try: container_id_1 = sc1.start(DATA) container_id_2 = sc2.start(DATA) containers = sc1.list_containers() # Both containers should be listed and contain the ID:s of two containers assert len(containers) == 2 assert containers.count(container_id_1) == 1 and containers.count( container_id_2) == 1 finally: sc1.terminate() sc2.terminate()
def test_set_caps_works(self): """ Test that setting an existing capability works, i.e. no error is returned """ sc = Container() try: sc.start(DATA) sc.set_capabilities(["test.cap.valid-dbus"]) finally: sc.terminate()
def test_set_caps_with_empty_arg_is_allowed(self): """ Test that there is no error when passing an empty list of caps, i.e. no caps """ sc = Container() try: sc.start(DATA) sc.set_capabilities([]) finally: sc.terminate()
def test_list_capabilities(self): """ Test listing available caps works as expected """ sc = Container() try: caps = sc.list_capabilities() # All caps present in the service manifest(s) set up previously should be listed assert "test.cap" in caps and "test.cap.valid-dbus" in caps and "test.cap.broken-gw-config" in caps # The default capabilities should not be visible in the list assert "test.cap.default.sample" not in caps finally: sc.terminate()
def test_non_existent_cap_results_in_failure(self): """ Test that setting a non-existing capability results in a failure """ sc = Container() try: sc.start(DATA) with pytest.raises(DBusException) as err: sc.set_capabilities(["does.not.exist"]) assert err.value.get_dbus_name() == Container.DBUS_EXCEPTION_FAILED finally: sc.terminate()
def test_setting_capability_with_broken_gw_config_fails(self): """ Setting a capability that leads to a broken gateway config should fail """ sc = Container() try: sc.start(DATA) with pytest.raises(DBusException) as err: sc.set_capabilities(["test.cap.broken-gw-config"]) assert err.value.get_dbus_name() == Container.DBUS_EXCEPTION_FAILED finally: sc.terminate()
def test_tmpfs_writebuffer_size_enabled_10mb(self): """ The SoftwareContainer will create a tmpfs in the location of the containers temporary directory location if the write buffer is enabled. Test that the tmpfs mounted in the containers temporary directory structure works as expected. This test will create a 10 MB tmpfs which will be tested that it behaves as expected. Files larger than 10MB can not be written and files smaller than 9MB can be written. """ absoluteTestFile = None ca = Container() DATA[Container.CONFIG] = '[{"writeBufferEnabled": true, \ "temporaryFileSystemWriteBufferEnabled": true,\ "temporaryFileSystemSize": ' + str(TenMB) + '}]' try: id = ca.start(DATA) absoluteTestFile = os.path.join("/tmp/container/SC-" + str(id) + "/rootfs-upper", TESTFILE) partitions = psutil.disk_partitions(all=True) interesting_partition = False for part in partitions: if part.mountpoint == '/tmp/container/SC-' + str(id): interesting_partition = part break assert interesting_partition is not False # Write a 9 MB file and make sure it is in the right size area (due # to how filesystems works, it may be a bit bigger or smaller so # exact comparison is not possible) with open(absoluteTestFile, "w") as f: f.write("w" * 9 * 1024 * 1024) # As mentioned exact comparison is not possible, so approximately # right at least.... assert os.path.getsize(absoluteTestFile) >= 8 * 1024 * 1024 assert os.path.getsize(absoluteTestFile) <= 10 * 1024 * 1024 # Try to write a file that is too big and it should raise an # IOError exception. with open(absoluteTestFile, "w") as f: with pytest.raises(IOError): f.write("w" * 105 * 1024 * 1024) finally: ca.terminate() if os.path.exists(absoluteTestFile): os.remove(absoluteTestFile)
def test_suspend_and_resume_works(self): """ Test suspending a container works. """ try: sc = Container() sc.start(DATA) sc.suspend() sc.resume() finally: sc.terminate()
def test_setting_capability_with_unkown_gw_id_fails(self): """ Setting a capability that results in a gateway config with an unknown gateway ID should fail """ sc = Container() try: sc.start(DATA) with pytest.raises(DBusException) as err: sc.set_capabilities(["test.cap"]) assert err.value.get_dbus_name() == Container.DBUS_EXCEPTION_FAILED finally: sc.terminate()
def test_tmpfs_writebuffer_size_enabled(self): """ The SoftwareContainer will create a tmpfs in the location of the containers temporary directory location if the write buffer is enabled. Test that the tmpfs mounted in the containers temporary directory structure works as expected. Note: The filesystem is defaults to 100MB, but is be configurable via the DATA field. This tests the default value. """ absoluteTestFile = None ca = Container() DATA[Container.CONFIG] = '[{"writeBufferEnabled": true}]' try: id = ca.start(DATA) absoluteTestFile = os.path.join("/tmp/container/SC-" + str(id) + "/rootfs-upper", TESTFILE) partitions = psutil.disk_partitions(all=True) interesting_partition = False for part in partitions: if part.mountpoint == '/tmp/container/SC-' + str(id): interesting_partition = part break assert interesting_partition is not False # Try to write 95 megabytes, this should be possible since the # default size of the tmpfs is 100 megabyte. with open(absoluteTestFile, "w") as f: f.write("w" * 95 * 1024 * 1024) # Make sure the file is between 94 and 96 megabyte (exact size # may vary depending on platform, blocksize etc) assert os.path.getsize(absoluteTestFile) >= 94 * 1024 * 1024 assert os.path.getsize(absoluteTestFile) <= 96 * 1024 * 1024 # Try to write a file that is too large for the filesystem (105 # megabytes). This should fail with an IOError. with open(absoluteTestFile, "w") as f: with pytest.raises(IOError): f.write("w" * 105 * 1024 * 1024) finally: ca.terminate() if os.path.exists(absoluteTestFile): os.remove(absoluteTestFile)
def test_list_containers_with_terminate(self): """ Test listing available containers works as expected if no containers are started and if containers have been started and then terminated """ sc1 = Container() sc2 = Container() sc3 = Container() try: # Verify the list of containers is empty before we start anything containers = sc1.list_containers() assert len(containers) == 0 container_id_1 = sc1.start(DATA) container_id_2 = sc2.start(DATA) # Both containers should be listed and contain the ID:s of two containers containers = sc1.list_containers() assert len(containers) == 2 assert containers.count(container_id_1) == 1 and containers.count( container_id_2) == 1 # Remove one of the containers and verify that it is not still in the list # Start a third container at the same time and verify that it is in the list container_id_3 = sc3.start(DATA) sc1.terminate() containers = sc1.list_containers() assert len(containers) == 2 assert containers.count(container_id_2) == 1 and containers.count( container_id_3) == 1 assert not containers.count(container_id_1) == 1 finally: try: sc1.terminate() # In case it didn't get terminated properly except (DBusException): pass sc2.terminate() sc3.terminate()
def test_dynamic_configuration(self, firstcap, secondcap): """ This function tests multiple activation of DeviceNodeGateway within given set of capabilities. The test is excpected to pass if there is no exception thwrown, otherwise it will fail. """ try: sc = Container() sc.start(DATA) sc.set_capabilities([firstcap]) sc.set_capabilities([secondcap]) finally: sc.terminate()
def test_memory_cgroup_small_threshold(self): """ Test if the memory limiting with cgroup gateway is working as expected which behavior is when allocated more memory than limit it should fail """ try: sc = Container() sc.start(DATA) with pytest.raises(DBusException) as err: sc.set_capabilities(["test.cap.small.threshold"]) assert err.value.get_dbus_name() == Container.DBUS_EXCEPTION_FAILED finally: sc.terminate()
def test_setting_same_var_fails(self): """ Setting a variable twice should result in failed call to set the capability that results in the bad config combo. """ sc = Container() try: sc.start(DATA) with pytest.raises(DBusException) as err: sc.set_capabilities(["environment.test.cap.1"]) assert err.value.get_dbus_name() == Container.DBUS_EXCEPTION_FAILED finally: sc.terminate()