Commit 3c405afd authored by Antonio Terceiro's avatar Antonio Terceiro
Browse files

dispatcher: add docker test action

Fixes #305
parent 044626eb
......@@ -115,7 +115,10 @@ def validate(data, strict=True, extra_context_variables=[]):
cls = "deploy." + data.get("to", "")
elif action_type == "test":
if "definitions" in data:
cls = "test.definition"
if "docker" in data:
cls = "test.docker"
else:
cls = "test.definition"
elif "interactive" in data:
cls = "test.interactive"
elif "monitors" in data:
......
# Copyright (C) 2020 Linaro Limited
#
# Author: Antonio Terceiro <antonio.terceiro@linaro.org>
#
# This file is part of LAVA.
#
# LAVA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LAVA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along
# with this program; if not, see <http://www.gnu.org/licenses>.
from voluptuous import Required
from lava_common.schemas.test.definition import schema as base
def schema():
docker = {Required("docker"): {Required("image"): str}}
return {**base(), **docker}
# Copyright (C) 2020 Linaro Limited
#
# Author: Antonio Terceiro <antonio.terceiro@linaro.org>
#
# This file is part of LAVA Dispatcher.
#
# LAVA Dispatcher is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LAVA Dispatcher is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along
# with this program; if not, see <http://www.gnu.org/licenses>.
import os
import shlex
from lava_dispatcher.action import Pipeline
from lava_dispatcher.actions.deploy.overlay import CreateOverlay
from lava_dispatcher.actions.test import TestAction
from lava_dispatcher.actions.test.shell import TestShellAction
from lava_dispatcher.logical import LavaTest
from lava_dispatcher.power import ReadFeedback
from lava_dispatcher.shell import ShellCommand, ShellSession
from lava_dispatcher_host import add_device_container_mapping
class DockerTest(LavaTest):
"""
DockerTest Strategy object
"""
priority = 10
def __init__(self, parent, parameters):
super().__init__(parent)
self.action = DockerTestAction()
self.action.job = self.job
self.action.section = self.action_type
parent.add_action(self.action, parameters)
@classmethod
def accepts(cls, device, parameters):
if "definition" in parameters or "definitions" in parameters:
if "docker" in parameters:
return True, "accepted"
return False, "docker or definition(s) not in parameters"
@classmethod
def needs_deployment_data(cls):
return False
@classmethod
def needs_overlay(cls):
return False
@classmethod
def has_shell(cls):
return True
class DockerTestAction(TestAction):
name = "lava-docker-test"
description = "Runs tests in a docker container"
summary = "Runs tests in a docker container, with the DUT available via adb/fastboot over USB"
def populate(self, parameters):
self.pipeline = Pipeline(parent=self, job=self.job, parameters=parameters)
self.pipeline.add_action(DockerTestSetEnvironment())
self.pipeline.add_action(CreateOverlay())
self.pipeline.add_action(DockerTestShell())
self.pipeline.add_action(ReadFeedback())
class GetBoardId:
def get_board_id(self):
device_info = self.job.device.get("device_info")
if not device_info:
return None
return device_info[0].get("board_id")
class DockerTestSetEnvironment(TestAction, GetBoardId):
name = "lava-docker-test-set-environment"
description = "Adds necessary environments variables for docker-test-shell"
summary = description
def run(self, connection, max_end_time):
environment = self.job.parameters.get("environment", {})
environment["ANDROID_SERIAL"] = self.get_board_id()
self.job.parameters["environment"] = environment
connection = super().run(connection, max_end_time)
return connection
class DockerTestShell(TestShellAction, GetBoardId):
name = "lava-docker-test-shell"
description = "Runs lava-test-shell in a docker container"
summary = "Runs lava-test-shell in a docker container"
def run(self, connection, max_end_time):
# obtain lava overlay
# start container
# create USB device mapping to container
# connect to container, and run lava-test-shell over it
location = self.get_namespace_data(
action="test", label="shared", key="location"
)
overlay = self.get_namespace_data(
action="test", label="results", key="lava_test_results_dir"
).strip("/")
image = self.parameters["docker"]["image"]
container = "lava-docker-test-shell-%s-%s" % (self.job.job_id, self.level)
board_id = self.get_board_id()
add_device_container_mapping(
job_id=self.job.job_id,
device_info={"board_id": board_id},
container=container,
container_type="docker",
logging_info=self.get_logging_info(),
)
mount_overlay = "--mount=type=bind,source=%s,destination=/%s" % (
os.path.join(location, overlay),
overlay,
)
docker_cmd = [
"docker",
"run",
"--rm",
mount_overlay,
"--interactive",
"--hostname=lava",
"--name=%s" % container,
"--env=PS1=docker-test-shell:$ ",
image,
"bash",
"--norc",
"-i",
]
cmd = " ".join([shlex.quote(s) for s in docker_cmd])
self.logger.debug("Starting docker test shell container: %s" % cmd)
shell = ShellCommand(cmd, self.timeout, logger=self.logger)
shell_connection = ShellSession(self.job, shell)
shell_connection.prompt_str = "docker-test-shell:"
self.__set_connection__(shell_connection)
super().run(shell_connection, max_end_time)
# return the original connection untouched
self.__set_connection__(connection)
return connection
def __set_connection__(self, c):
self.set_namespace_data(
action="shared",
label="shared",
key="connection",
value=c,
parameters=self.parameters,
)
......@@ -27,3 +27,4 @@ from lava_dispatcher.actions.test.shell import TestShell
from lava_dispatcher.actions.test.multinode import MultinodeTestShell
from lava_dispatcher.actions.test.monitor import TestMonitor
from lava_dispatcher.actions.test.interactive import TestInteractive
from lava_dispatcher.actions.test.docker import DockerTestShell
# Copyright (C) 2020 Linaro Limited
#
# Author: Antonio Terceiro <antonio.terceiro@linaro.org>
#
# This file is part of LAVA Dispatcher.
#
# LAVA Dispatcher is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LAVA Dispatcher is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along
# with this program; if not, see <http://www.gnu.org/licenses>.
import pytest
import re
import time
from pathlib import Path
from tests.lava_dispatcher.test_basic import Factory
@pytest.fixture
def factory():
return Factory()
@pytest.fixture
def job(factory):
return factory.create_job(
"hi6220-hikey-r2-01.jinja2", "sample_jobs/docker-test.yaml"
)
@pytest.fixture
def action(job):
return job.pipeline.actions[2]
def test_validate_schema(factory):
factory.validate_job_strict = True
# The next call not crashing means that the strict schema validation
# passed.
factory.create_job("hi6220-hikey-r2-01.jinja2", "sample_jobs/docker-test.yaml")
def test_detect_correct_action(action):
assert type(action).__name__ == "DockerTestAction"
def test_run(action, mocker):
mocker.patch("lava_dispatcher.utils.fastboot.DockerDriver.__get_device_nodes__")
ShellCommand = mocker.patch("lava_dispatcher.actions.test.docker.ShellCommand")
ShellSesssion = mocker.patch("lava_dispatcher.actions.test.docker.ShellSession")
docker_connection = mocker.MagicMock()
ShellSesssion.return_value = docker_connection
action_run = mocker.patch("lava_dispatcher.actions.test.docker.TestShellAction.run")
connection = mocker.MagicMock()
add_device_container_mapping = mocker.patch(
"lava_dispatcher.actions.test.docker.add_device_container_mapping"
)
action.validate()
action.run(connection, time.time() + 1000)
# device is shared with the container
add_device_container_mapping.assert_called_with(
job_id=action.job.job_id,
device_info={"board_id": "0123456789"},
container=mocker.ANY,
container_type="docker",
logging_info=mocker.ANY,
)
# overlay gets created
overlay = next(Path(action.job.tmp_dir).glob("lava-create-overlay-*/lava-*"))
assert overlay.exists()
# overlay gets the correct content
lava_test_runner = overlay / "bin" / "lava-test-runner"
assert lava_test_runner.exists()
lava_test_0 = overlay / "0"
assert lava_test_runner.exists()
environment = overlay / "environment"
assert "ANDROID_SERIAL='0123456789'" in environment.open().read()
# docker gets called
docker_call = ShellCommand.mock_calls[0][1][0]
assert docker_call.startswith("docker run")
# overlay gets passed into docker
assert (
re.match(
r".* --mount=type=bind,source=%s,destination=/%s" % (overlay, overlay.name),
docker_call,
)
is not None
)
# the lava-test-shell implementation gets called with the docker shell
action_run.assert_called_with(docker_connection, mocker.ANY)
device_type: hi6220-hikey-r2
job_name: AOSP hikey deploy/boot/test with docker
timeouts:
job:
minutes: 60
action:
minutes: 15
connection:
minutes: 2
priority: medium
visibility: public
metadata:
source: https://lkft.validation.linaro.org/scheduler/job/972536/definition
actions:
- deploy:
timeout:
minutes: 15
# AOSP deployment
to: fastboot
docker:
image: adb-fastboot
images:
boot:
url: http://localhost:8888/aosp/hikey/boot.img
reboot: hard-reset
- boot:
docker:
image: adb-fastboot
prompts:
- 'healthd: No battery devices found'
- 'hikey: '
- 'console:'
timeout:
minutes: 15
method: fastboot
- test:
timeout:
minutes: 10
docker:
image: adb-fastboot
definitions:
- repository:
metadata:
format: Lava-Test Test Definition 1.0
name: smoke-tests-basic
description: "Basic system test command for Linaro Ubuntu images"
os:
- debian
scope:
- functional
devices:
- hi6220-hikey-r2
run:
steps:
- date
- hostname
- adb devices
- adb shell date
- adb shell hostname
from: inline
path: inline-smoke-test
name: docker-test
......@@ -159,6 +159,8 @@ class Factory:
)
)
validate_job_strict = False
def prepare_jinja_template(self, hostname, jinja_data):
string_loader = jinja2.DictLoader({"%s.jinja2" % hostname: jinja_data})
type_loader = jinja2.FileSystemLoader([self.DEVICE_TYPES_PATH])
......@@ -213,7 +215,7 @@ class Factory:
def create_custom_job(self, template, job_data, job_ctx=None, validate=True):
if validate:
validate_job(job_data, strict=False)
validate_job(job_data, strict=self.validate_job_strict)
if job_ctx:
job_data["context"] = job_ctx
else:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment