Skip to content

Commit

Permalink
scenario_execution library for docker interaction (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmirus authored Oct 11, 2024
1 parent dd19304 commit e4c55cd
Show file tree
Hide file tree
Showing 29 changed files with 1,199 additions and 2 deletions.
4 changes: 3 additions & 1 deletion docs/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ Kubernetes
yaml
absolutized
moveit
replan
replan
effector
mnt
161 changes: 160 additions & 1 deletion docs/libraries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Beside ``osc.standard`` provided by OpenSCENARIO 2 (which we divide into ``osc.s

* - Name
- Description
* - ``osc.docker``
- Docker Library (provided with :repo_link:`libs/scenario_execution_docker`)
* - ``osc.gazebo``
- Gazebo Library (provided with :repo_link:`libs/scenario_execution_gazebo`)
* - ``osc.helpers``
Expand All @@ -32,6 +34,163 @@ Beside ``osc.standard`` provided by OpenSCENARIO 2 (which we divide into ``osc.s
Additional features can be implemented by defining your own library.


Docker
------

The library contains actions to interact with `Docker <https://www.docker.com/>`_. Import it with ``import osc.docker``. It's provided by the package :repo_link:`libs/scenario_execution_docker`.

``docker_run()``
^^^^^^^^^^^^^^^^

Runs a Docker container

.. list-table::
:widths: 15 15 5 65
:header-rows: 1
:class: tight-table

* - Parameter
- Type
- Default
- Description
* - ``image``
- ``string``
-
- The image to run
* - ``command``
- ``string``
-
- The command to run in the container
* - ``container_name``
- ``string``
-
- The name for this container
* - ``detach``
- ``bool``
- false
- Whether to run container in the background
* - ``environment``
- ``list of string``
-
- Environment variables to set inside the container, i.e., a list of strings in the format ["SOMEVARIABLE=xxx"].
* - ``network``
- ``string``
-
- Name of the network this container will be connected to at creation time
* - ``privileged``
- ``bool``
- false
- Give extended privileges to this container
* - ``remove``
- ``bool``
- true
- Remove the container when it as finished running
* - ``stream``
- ``bool``
- true
- If true and detach is false, return a log generator instead of a string. Ignored if detach is true.
* - ``volumes``
- ``list of string``
-
- A list of strings which each one of its elements specifies a mount volume: ['/home/user1/:/mount/vol2','/home/user2/:/mount/vol1']

``docker_exec()``
^^^^^^^^^^^^^^^^^

Runs a command inside a given Docker container

.. list-table::
:widths: 15 15 5 65
:header-rows: 1
:class: tight-table

* - Parameter
- Type
- Default
- Description
* - ``container``
- ``string``
-
- The name or id of the container to run the command in
* - ``container``
- ``string``
-
- The name or id of the container to run the command in
* - ``command``
- ``string``
-
- The command to run inside the container
* - ``environment``
- ``list of string``
-
- Environment variables to set inside the container, i.e., a list of strings in the format ["SOMEVARIABLE=xxx"].
* - ``privileged``
- ``bool``
- false
- Give extended privileges to this container
* - ``user``
- ``string``
- root
- User to execute command as
* - ``workdir``
- ``string``
-
- Path to working directory for this exec session

``docker_copy()``
^^^^^^^^^^^^^^^^^

Copy a file or folder from the container.
Note that this actions potentially blocks other action calls if the copied content is large.
In case large files or folders need to be copied, consider mounting a volume to the container instead of this action.

.. list-table::
:widths: 15 15 5 65
:header-rows: 1
:class: tight-table

* - Parameter
- Type
- Default
- Description
* - ``container``
- ``string``
-
- The name or id of the container to run the command in
* - ``file_path``
- ``string``
-
- Path to the file or folder inside the container to retrieve

``docker_put()``
^^^^^^^^^^^^^^^^^

Copy a file or folder from the local system into a running container.
Note that this actions potentially blocks other action calls if the copied content is large.
In case large files or folders need to be copied, consider mounting a volume to the container instead of this action.

.. list-table::
:widths: 15 15 5 65
:header-rows: 1
:class: tight-table

* - Parameter
- Type
- Default
- Description
* - ``container``
- ``string``
-
- The name or id of the container to put the file or folder into
* - ``source_path``
- ``string``
-
- Path to the file or folder in the local system to copy
* - ``target_path``
- ``string``
-
- Target path inside the container to put the file or folder

Gazebo
------

Expand Down Expand Up @@ -1604,4 +1763,4 @@ Capture the screen content within a video.
* - ``frame_rate``
- ``float``
- ``25.0``
- Frame-rate of the resulting video
- Frame-rate of the resulting video
1 change: 1 addition & 0 deletions libs/scenario_execution_docker/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include scenario_execution_docker/lib_osc/*.osc
8 changes: 8 additions & 0 deletions libs/scenario_execution_docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Scenario Execution Library for Docker interaction

The `scenario_execution_docker` package provides actions to interact with docker.

It provides the following scenario execution library:

- `docker.osc`: Actions to interact with docker

23 changes: 23 additions & 0 deletions libs/scenario_execution_docker/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>scenario_execution_docker</name>
<version>1.2.0</version>
<description>Scenario Execution library for docker interactions</description>
<author email="[email protected]">Intel Labs</author>
<maintainer email="[email protected]">Intel Labs</maintainer>
<license file="../../LICENSE">Apache-2.0</license>

<depend>scenario_execution</depend>

<exec_depend>python3-docker</exec_depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (C) 2024 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (C) 2024 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright (C) 2024 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0

from enum import Enum

import os
import docker
import tempfile
import tarfile
import py_trees
from scenario_execution.actions.base_action import BaseAction, ActionError


class CopyStatus(Enum):
IDLE = 1
FOUND_CONTAINER = 2
COPYING = 3
DONE = 4


class DockerCopy(BaseAction):
"""
Copy a file or folder from a running container
"""

def __init__(self, container: str, file_path: str):
super().__init__()
self.container = container
self.file_path = file_path

self.container_object = None
self.current_state = CopyStatus.IDLE
self.output_dir = None
self.client = None
self.result_data = None

def setup(self, **kwargs):
# create docker client
self.client = docker.from_env()

# check output_dir
if "output_dir" not in kwargs:
raise ActionError("output_dir not defined.", action=self)

if kwargs['output_dir']:
if not os.path.exists(kwargs['output_dir']):
raise ActionError(f"Specified destination dir '{kwargs['output_dir']}' does not exist", action=self)
self.output_dir = kwargs['output_dir']

def update(self) -> py_trees.common.Status:
if self.current_state == CopyStatus.IDLE:
try:
self.container_object = self.client.containers.get(self.container)
self.current_state = CopyStatus.FOUND_CONTAINER
except docker.errors.APIError as e:
self.feedback_message = f"Docker container {self.container} not yet running {e}" # pylint: disable= attribute-defined-outside-init
return py_trees.common.Status.RUNNING

if self.current_state == CopyStatus.FOUND_CONTAINER:
try:
self.result_data, _ = self.container_object.get_archive(
path=self.file_path)
self.current_state = CopyStatus.COPYING
self.feedback_message = f"Copying data from path {self.file_path} in container {self.container} to {self.output_dir}" # pylint: disable= attribute-defined-outside-init
except docker.errors.APIError as e:
self.feedback_message = f"Copying of data from path {self.file_path} failed: {e}" # pylint: disable= attribute-defined-outside-init
return py_trees.common.Status.FAILURE

if self.current_state == CopyStatus.COPYING:
output_tar = tempfile.NamedTemporaryFile(suffix=".tar")
try:
with open(output_tar.name, 'wb') as f:
for chunk in self.result_data:
f.write(chunk)
with tarfile.open(output_tar.name, 'r') as tar:
tar.extractall(self.output_dir)
self.current_state = CopyStatus.DONE
except tarfile.ReadError as e:
self.feedback_message = f"Copying of data from path {self.file_path} failed: {e}" # pylint: disable= attribute-defined-outside-init
return py_trees.common.Status.FAILURE

if self.current_state == CopyStatus.DONE:
self.feedback_message = f"Finished copying of data from path {self.file_path} to {self.output_dir}" # pylint: disable= attribute-defined-outside-init
return py_trees.common.Status.SUCCESS

return py_trees.common.Status.RUNNING
Loading

0 comments on commit e4c55cd

Please sign in to comment.