This repository was archived by the owner on Sep 7, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 32
Feature/ppm 30 boardfarm docker compose #1587
Open
odkq
wants to merge
36
commits into
master
Choose a base branch
from
feature/PPM-30-boardfarm-docker-compose
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
2a778b5
PPM-30: Changes on class ALEntityDocker for compose
odkq 9f26b8a
PPM-30: Define test entrypoint and json config
odkq bd59982
PPM-30: Dockerfile and docker-compose.yml
odkq 1444905
PPM-30: dctest.py script to wrap docker-compose
odkq 856854b
PPM-30: Add test to boardfarm-ci
odkq bb1645d
PPM-30: Check return error on _run_shell
odkq 3d819a8
Added device prplmesh_compose.py
odkq c108e2f
boardfarm: use the name of the container for the status check
rmelotte 7ec7a92
Use original name and docker-name for hostname in environment.py
odkq 61cc3b7
boardfarm-ci: Remove unused code
odkq dd55701
boardfarm-ci: Commented and reordered Dockerfile
odkq 0e1458d
boardfarm-ci: Scan device names
odkq d53c32a
boardfarm-ci: Fixes for PEP-8 compliance
odkq a0d8fe7
boardfarm-ci: Remove unused code
odkq 727d836
boardfarm-ci: Remove commented out line
odkq 9355d53
boardfarm-ci: Removed more unused code
odkq 44af58b
boardfarm-ci: Removed commented out code
odkq 28e53f2
boardfarm-ci: Removed commented-out code
odkq 1df2526
boardfarm-ci: Use relative path
odkq 0042f0e
boardfarm-ci: Added mutual exclusion in argv
odkq e9d90a3
tests: ALEntity: use name in place of device.docker_name
rmelotte a999708
boardfarm-ci: Group all apt-get commands together
odkq a0ae26c
boardfarm-ci: Removed unused CURRENT_ID
odkq 157e5e4
boardfarm-ci: Commented on issue PPM-208
odkq 344ebfe
boardfarm-ci: Better error message on _run_cmd
odkq b4adb53
boardfarm-ci: Fixed check_status
odkq d0e1415
boardfarm-ci: Set delay to the default 7 seconds
odkq c34ceeb
boardfarm-ci: Restored comment on ALEntityDocker's contructor
odkq 599fc48
boardfarm-ci: Added license header to dctest.py
odkq 356793b
boardfarm-ci: Reorder lines to improve readability
odkq af13310
boardfarm-ci: Fixed typo
odkq 09e27fb
boardfarm-ci: Aesthetical changes to pass PEP-8
odkq a13b772
boardfarm-ci: Resolved merge conflict in Dockerfile
odkq 62a8125
Merge branch 'master' into feature/PPM-30-boardfarm-docker-compose
odkq edc4b0a
boardfarm-ci: Added missing jq
odkq df60f75
boardfarm-ci: Fixes for the new prplmesh/prplmesh URL
odkq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,226 @@ | ||
| #!/usr/bin/env python3 | ||
| # | ||
| # SPDX-License-Identifier: BSD-2-Clause-Patent | ||
| # | ||
| # SPDX-FileCopyrightText: 2019-2020 the prplMesh contributors (see AUTHORS.md) | ||
| # | ||
| # This code is subject to the terms of the BSD+Patent license. | ||
| # | ||
| # See LICENSE file for more details. | ||
| # | ||
| # Launch the test suite using docker and docker-compose. This script wraps | ||
| # the creation of the bridge(s) to be able to connect external devices with | ||
| # the docker network, launching the service for boardfarm. | ||
| # | ||
| # As this script is run outside containers, it does not use anything apart | ||
| # from Python 3.5 (will work on later versions but only uses 3.5 features) | ||
| # | ||
| # The best way to make sure no Python 3.5+ features are used is running the | ||
| # script with a Python 3.5.0 interpreter. Compile it from: | ||
| # | ||
| # https://www.python.org/ftp/python/3.5.0/Python-3.5.0.tgz | ||
| # | ||
| # Also, when calling a function look for 'New in version 3.X' where X > 5 | ||
| # | ||
| from __future__ import print_function # To check for python2 or < 3.5 execution | ||
| import argparse | ||
| import os | ||
| import grp | ||
| import getpass | ||
| import sys | ||
| import json | ||
| from subprocess import Popen, PIPE | ||
|
|
||
|
|
||
| if not (sys.version_info.major == 3 and sys.version_info.minor >= 5): | ||
| print("This script requires Python 3.5 or higher!") | ||
| print("You are using Python {}.{}.".format(sys.version_info.major, sys.version_info.minor)) | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| def check_docker_versions(): | ||
| DOCKER_MAJOR = 19 | ||
| DC_MAJOR = 1 | ||
| DC_MINOR = 25 | ||
| docker_version = os.popen('docker --version').read().split(' ')[2] | ||
| docker_major = int(docker_version.split('.')[0]) | ||
| if docker_major < DOCKER_MAJOR: | ||
| fmt = "This script requires docker {}.0 or higher" | ||
| print(fmt.format(DOCKER_MAJOR)) | ||
| print("You are usng version {}".format(docker_version)) | ||
| sys.exit(1) | ||
| dc_version = os.popen('docker-compose --version').read().split(' ')[2] | ||
| dc_major = int(dc_version.split('.')[0]) | ||
| dc_minor = int(dc_version.split('.')[1]) | ||
| if dc_major < DC_MAJOR: | ||
| fmt = "This script requires docker-compose {}.{} or higher" | ||
| print(fmt.format(DC_MAJOR, DC_MINOR)) | ||
| print("You are usng version {}".format(dc_version)) | ||
| sys.exit(1) | ||
| if dc_minor < DC_MINOR: | ||
| fmt = "This script requires docker-compose {}.{} or higher" | ||
| print(fmt.format(DC_MAJOR, DC_MINOR)) | ||
| print("You are usng version {}".format(dc_version)) | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| class Services: | ||
| def __init__(self, bid=None): | ||
| self.scriptdir = os.path.dirname(os.path.realpath(__file__)) | ||
| os.chdir(self.scriptdir) | ||
| self.rootdir = self.scriptdir | ||
|
|
||
| if bid is not None: | ||
| self.build_id = bid | ||
| print('Using ID {}'.format(self.build_id)) | ||
| # return | ||
| else: | ||
| self.build_id = self.get_build_id() | ||
|
|
||
| self.logdir = os.path.join(self.scriptdir, 'logs') | ||
| if not os.path.exists(self.logdir): | ||
| os.makedirs(self.logdir) | ||
| for device in self._get_device_names(): | ||
| device_name = '{}-{}'.format(device, self.build_id) | ||
| devicedir = os.path.join(self.logdir, device_name) | ||
| if not os.path.exists(devicedir): | ||
| print('Making {}'.format(devicedir)) | ||
| os.makedirs(devicedir) | ||
|
|
||
| def _get_device_names(self): | ||
| jspath = './tests/boardfarm_plugins/boardfarm_prplmesh/prplmesh_config_compose.json' | ||
| js = json.loads(open(jspath, 'r').read()) | ||
| devices = [] | ||
| for device in js['prplmesh_compose']['devices']: | ||
| devices.append(device['name']) | ||
| return devices | ||
|
|
||
| def get_build_id(self): | ||
| ci_pipeline_id = os.getenv('CI_PIPELINE_ID') | ||
| if ci_pipeline_id is not None: | ||
| return ci_pipeline_id | ||
|
|
||
| # Otherwise we are running on the local machine, just find last id | ||
| # created and add one | ||
| last_id = 0 | ||
| if not os.path.exists('logs'): | ||
| return str(1) | ||
|
|
||
| # Search if a directory exists with logs/<device>-<X> and use X+1 as | ||
| # id. Get the first device from the json list | ||
| search_prefix = self._get_device_names()[0] + '-' | ||
| for d in os.listdir('logs'): | ||
| if d.startswith(search_prefix): | ||
| suffix = d[len(search_prefix):] | ||
| isuffix = int(suffix) | ||
| if isuffix > last_id: | ||
| last_id = isuffix | ||
| if last_id == 0: | ||
| new_id = 1 | ||
| else: | ||
| new_id = last_id + 1 | ||
| return str(new_id) | ||
|
|
||
| def dc(self, args, interactive=False): | ||
| params = ['docker-compose', '-f', | ||
| 'tools/docker/boardfarm-ci/docker-compose.yml'] | ||
| params += args | ||
| local_env = os.environ | ||
| local_env['ROOT_DIR'] = self.rootdir | ||
| docker_gid = grp.getgrnam('docker')[2] | ||
| local_env['CURRENT_UID_GID'] = str(os.getuid()) + ':' + str(docker_gid) | ||
| local_env['RUN_ID'] = self.build_id | ||
|
|
||
| if os.getenv('CI_PIPELINE_ID') is None: | ||
| # Running locally | ||
| local_env['CI_PIPELINE_ID'] = 'latest' | ||
| local_env['FINAL_ROOT_DIR'] = self.rootdir | ||
| else: | ||
| # Running inside gitlab-ci | ||
| # Setting a fixed location is needed until | ||
| # https://jira.prplfoundation.org/browse/PPM-208 is fixed. | ||
| local_env['FINAL_ROOT_DIR'] = '/builds/prpl-foundation/prplmesh/prplMesh' | ||
|
|
||
| if not interactive: | ||
| proc = Popen(params, stdout=PIPE, stderr=PIPE) | ||
| for line in proc.stdout: | ||
| print(line.decode(), end='') | ||
| proc.stdout.close() | ||
| else: | ||
| proc = Popen(params) | ||
| return_code = proc.wait() | ||
| return return_code | ||
|
|
||
|
|
||
| def cleanup(rc): | ||
| if rc != 0: | ||
| print('Return code !=0 -> {}'.format(rc)) | ||
| if getpass.getuser() == 'gitlab-runner': | ||
| os.system('chown -R gitlab-runner:gitlab-runner .') | ||
| sys.exit(rc) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| check_docker_versions() | ||
| parser = argparse.ArgumentParser(description='Dockerized test launcher') | ||
| group = parser.add_mutually_exclusive_group() | ||
| group.add_argument('--test', dest='test', type=str, help='Test to be run') | ||
| group.add_argument('--clean', dest='clean', action='store_true', | ||
| help='Clean containers images and networks') | ||
| group.add_argument('--build', dest='build', action='store_true', | ||
| help='Rebuild containers') | ||
| group.add_argument('--shell', dest='shell', action='store_true', | ||
| help='Run a shell on the bf container') | ||
| group.add_argument('--comp', dest='comp', action='store_true', | ||
| help='Pass the rest of arguments to docker-compose') | ||
| parser.add_argument('--id', dest='bid', type=str, | ||
| help='Specify the id to use for build/shell/comp/clean') | ||
| args, rest = parser.parse_known_args() | ||
|
|
||
| if os.getenv('CI_PIPELINE_ID') is not None: | ||
| args.bid == os.getenv('CI_PIPELINE_ID') | ||
|
|
||
| if args.comp: | ||
| if args.bid is None: | ||
| print('Specify --id for the --comp parameter') | ||
| sys.exit(0) | ||
| services = Services(bid=args.bid) | ||
| if len(rest) == 0: | ||
| print('Usage: dctest --id <id> --comp <arguments to docker-compose>') | ||
| sys.exit(1) | ||
| sys.exit(services.dc(rest, interactive=True)) | ||
| else: | ||
| if len(rest) > 0: | ||
| print('Unknown parameters: {}'.format(rest)) | ||
| sys.exit(1) | ||
|
|
||
| if args.clean: | ||
| if args.bid is None: | ||
| print('Specify --id for the --clean parameter') | ||
| sys.exit(0) | ||
| services = Services(bid=args.bid) | ||
| rc = services.dc(['down', '--remove-orphans', '--rmi', 'all']) | ||
| cleanup(rc) | ||
| elif args.shell: | ||
| if not args.bid: | ||
| print('Specify --id for the shell parameter') | ||
| sys.exit(0) | ||
| services = Services(bid=args.bid) | ||
| rc = services.dc(['run', '--rm', '--service-ports', '--entrypoint', | ||
| '/bin/bash', 'boardfarm'], interactive=True) | ||
| cleanup(rc) | ||
| elif args.build: | ||
| if not args.bid: | ||
| print('Specify --id for the build parameter') | ||
| sys.exit(0) | ||
| services = Services(bid=args.bid) | ||
| rc = services.dc(['build'], interactive=True) | ||
| cleanup(rc) | ||
| else: | ||
| if args.bid: | ||
| services = Services(bid=args.bid) # With new build id | ||
| else: | ||
| services = Services() # With new build id | ||
| rc = services.dc(['run', '--rm', '--service-ports', '--use-aliases', | ||
| 'boardfarm'], interactive=True) | ||
| cleanup(rc) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
tests/boardfarm_plugins/boardfarm_prplmesh/devices/prplmesh_compose.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # SPDX-License-Identifier: BSD-2-Clause-Patent | ||
| # SPDX-FileCopyrightText: 2020 the prplMesh contributors (see AUTHORS.md) | ||
| # This code is subject to the terms of the BSD+Patent license. | ||
| # See LICENSE file for more details. | ||
|
|
||
| import os | ||
| import time | ||
|
|
||
| import boardfarm | ||
| from environment import ALEntityDocker, _get_bridge_interface | ||
| from .prplmesh_base import PrplMeshBase | ||
| from sniffer import Sniffer | ||
|
|
||
| rootdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../..')) | ||
|
|
||
|
|
||
| class PrplMeshCompose(PrplMeshBase): | ||
| """Dockerized prplMesh device.""" | ||
|
|
||
| model = ("prplmesh_compose") | ||
| agent_entity = None | ||
| controller_entity = None | ||
|
|
||
| def __init__(self, *args, **kwargs): | ||
| self.args = args | ||
| self.kwargs = kwargs | ||
|
|
||
| config = kwargs.get("config", kwargs) | ||
|
|
||
| # List of device's consoles test can interact with | ||
| self.consoles = [self] | ||
odkq marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # Getting unic ID to distinguish devices and network they belong to | ||
| self.unique_id = os.getenv("RUN_ID") | ||
| self.user_id = os.getenv("SUDO_USER", os.getenv("USER", "")) | ||
|
|
||
| self.name = config.get("name", "prplmesh_compose") | ||
| self.docker_name = "-".join((self.name, self.unique_id)) | ||
| print('config.get("name") {}'.format(config.get("name"))) | ||
| self.role = config.get("role", "agent") | ||
| self.cleanup_cmd = config.get("cleanup_cmd", None) | ||
| self.conn_cmd = config.get("conn_cmd", None) | ||
| self.delay = config.get("delay", 7) | ||
| self.docker_network = "boardfarm-ci_default" | ||
odkq marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if self.role == "controller": | ||
| self._docker_compose(["-d", "--name", self.docker_name, "controller"], | ||
| "run", "start-controller-agent") | ||
| time.sleep(self.delay) | ||
| self.controller_entity = \ | ||
| ALEntityDocker(self.docker_name, device=self, is_controller=True, compose=True) | ||
| else: | ||
| self._docker_compose(["-d", "--name", self.docker_name, "agent"], | ||
| "run", "start-agent") | ||
| time.sleep(self.delay) | ||
| self.agent_entity = ALEntityDocker(self.docker_name, device=self, | ||
| is_controller=False, compose=True) | ||
|
|
||
| self.wired_sniffer = Sniffer(_get_bridge_interface(self.docker_network), | ||
| boardfarm.config.output_dir) | ||
| self.check_status() | ||
|
|
||
| def _docker_compose(self, args, parameter=None, start=None): | ||
| print('_docker_compose: args {}'.format(args)) | ||
| yml_path = "tools/docker/boardfarm-ci/docker-compose.yml" | ||
| full_args = ["-f", os.path.join(rootdir, yml_path)] | ||
| if parameter == "run": | ||
| log_path = os.path.join(rootdir, "logs/{}".format(self.docker_name)) | ||
| if not os.path.exists(log_path): | ||
| os.mkdir(log_path) | ||
|
|
||
| pipeline_id = os.getenv('CI_PIPELINE_ID') | ||
| if pipeline_id is None or pipeline_id == 'latest': | ||
| vol = '{}:/tmp/{}/beerocks/logs'.format(log_path, self.user_id) | ||
| else: | ||
| vol = '{}:/tmp/beerocks/logs'.format(log_path) | ||
|
|
||
| full_args += ["run", "--rm", "-v", vol] | ||
| full_args += args | ||
|
|
||
| print('_docker_compose: {}'.format(' '.join(full_args))) | ||
| if os.getenv('CI_PIPELINE_ID') is None: | ||
| print('Setting CI_PIPELINE_ID "latest"') | ||
| os.environ['CI_PIPELINE_ID'] = 'latest' | ||
| self._run_shell_cmd("docker-compose", | ||
| full_args, env=os.environ) | ||
| else: | ||
| self._run_shell_cmd("docker-compose", full_args) | ||
|
|
||
| def __del__(self): | ||
| self._run_shell_cmd("docker", ["stop", self.docker_name]) | ||
| self._run_shell_cmd("docker", ["container", "rm", "-f", self.docker_name]) | ||
|
|
||
| def check_status(self): | ||
| """Method required by boardfarm. | ||
|
|
||
| It is used by boardfarm to indicate that spawned device instance is ready for test | ||
| and also after test - to insure that device still operational. | ||
| """ | ||
| self._run_shell_cmd("printf", | ||
| ["device_get_info", "|", "nc", "-w", "1", self.docker_name, "8002"]) | ||
|
|
||
| def isalive(self): | ||
| """Method required by boardfarm. | ||
|
|
||
| States that device is operational and its consoles are accessible. | ||
|
|
||
| """ | ||
| return True | ||
|
|
||
| def prprlmesh_status_check(self): | ||
| self.check_status() | ||
odkq marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return True | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.