Skip to content

Commit

Permalink
Testing VagrantBackend, fixed DockerBackend (after docker-py upgrade)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikicz committed Feb 6, 2018
1 parent d7c7381 commit 1760430
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 18 deletions.
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ notifications:
sudo: required
services:
- docker

install:
- python setup.py install
before_script:
Expand All @@ -24,7 +23,10 @@ before_script:
- "./cc-test-reporter before-build"
- docker login -u "$DOCKER_HUB_USERNAME" -p "$DOCKER_HUB_PASSWORD"
script:
- python setup.py test
- python setup.py test 2>error.log
- echo "==============================="
- echo "Captured error out from tests"
- cat error.log
after_script:
- "./cc-test-reporter after-build -t coverage.py"
deploy:
Expand Down
6 changes: 3 additions & 3 deletions arca/backend/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import docker
import docker.errors
from docker.models.containers import Container
from docker.models.containers import Container, ExecResult
from docker.models.images import Image
from git import Repo
from requests.exceptions import ConnectionError
Expand Down Expand Up @@ -477,9 +477,9 @@ def run(self, repo: str, branch: str, task: Task, git_repo: Repo, repo_path: Pat
container.put_archive("/srv/scripts", self.tar_script(script_name, script))

try:
res = container.exec_run(["python", f"/srv/scripts/{script_name}"], tty=True)
res: ExecResult = container.exec_run(["python", f"/srv/scripts/{script_name}"], tty=True)

return Result(json.loads(res))
return Result(json.loads(res.output))
except Exception as e:
logger.exception(e)
raise BuildError("The build failed", extra_info={
Expand Down
52 changes: 42 additions & 10 deletions arca/backend/vagrant.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import json
import re
import subprocess
from pathlib import Path
from uuid import uuid4

from fabric import api
from git import Repo
from vagrant import Vagrant, make_file_cm

from arca.task import Task
from arca.result import Result
from arca.exceptions import ArcaMisconfigured, BuildError
from arca.result import Result
from arca.task import Task
from arca.utils import LazySettingProperty, logger
from .docker import DockerBackend

from vagrant import Vagrant
from fabric import api


@api.task
def run_script(container_name, script_name):
Expand All @@ -25,16 +27,25 @@ def run_script(container_name, script_name):

class VagrantBackend(DockerBackend):

box = LazySettingProperty(key="box", default="ubuntu/trusty64")
# Box has to either not contain docker at all (will be installed in that case, takes a long time)
# or has to contain Docker with version >= 1.8 (Versions < 1.8 can't copy files from host to container)
box = LazySettingProperty(key="box", default="ailispaw/barge")
provider = LazySettingProperty(key="provider", default="virtualbox")
quiet = LazySettingProperty(key="quiet", default=False, convert=bool)
quiet = LazySettingProperty(key="quiet", default=True, convert=bool)
destroy = LazySettingProperty(key="destroy", default=True, convert=bool)

def validate_settings(self):
super(VagrantBackend, self).validate_settings()

if self.push_to_registry_name is None:
raise ArcaMisconfigured("Push to registry setting is required for VagrantBackend")

if not re.match(r"^[a-z]+/[a-zA-Z0-9\-_]+$", self.box):
raise ArcaMisconfigured("Provided Vagrant box is not valid")

if not re.match(r"^[a-z_]+$", self.provider):
raise ArcaMisconfigured("Provided Vagrant provider is not valid")

def get_vagrant_file_location(self, repo: str, branch: str, git_repo: Repo, repo_path: Path) -> Path:
""" Returns a directory where Vagrantfile should be. Based on repo, branch and tag of the used docker image.
"""
Expand All @@ -59,6 +70,8 @@ def create_vagrant_file(self, repo: str, branch: str, git_repo: Repo, repo_path:
image_tag = self.get_image_tag(requirements_file, dependencies)
image_name = self.push_to_registry_name

logger.info("Creating Vagrantfile with image %s:%s", image_name, image_tag)

container_name = "arca_{}_{}_{}".format(
self._arca.repo_id(repo),
branch,
Expand All @@ -74,6 +87,7 @@ def create_vagrant_file(self, repo: str, branch: str, git_repo: Repo, repo_path:
Vagrant.configure("2") do |config|
config.vm.box = "{self.box}"
config.ssh.insert_key = true
config.vm.provision "docker" do |d|
d.pull_images "{image_name}:{image_tag}"
d.run "{image_name}:{image_tag}",
Expand All @@ -82,6 +96,7 @@ def create_vagrant_file(self, repo: str, branch: str, git_repo: Repo, repo_path:
cmd: "bash -i"
end
config.vm.synced_folder ".", "/vagrant"
config.vm.synced_folder "{repo_path}", "/srv/data"
config.vm.provider "{self.provider}"
Expand All @@ -94,8 +109,11 @@ def run(self, repo: str, branch: str, task: Task, git_repo: Repo, repo_path: Pat
vagrant_file = self.get_vagrant_file_location(repo, branch, git_repo, repo_path)

if not vagrant_file.exists():
logger.info("Vagrantfile doesn't exist, creating")
self.create_vagrant_file(repo, branch, git_repo, repo_path)

logger.info("Vagrantfile in folder %s", vagrant_file)

script_name, script = self.create_script(task)

(vagrant_file / script_name).write_text(script)
Expand All @@ -106,12 +124,23 @@ def run(self, repo: str, branch: str, task: Task, git_repo: Repo, repo_path: Pat
self._arca.current_git_hash(repo, branch, git_repo, short=True)
)

vagrant = Vagrant(root=vagrant_file, quiet_stdout=self.quiet, quiet_stderr=self.quiet)
vagrant.up()
log_path = Path(self._arca.base_dir) / "logs" / (str(uuid4()) + ".log")
log_path.parent.mkdir(exist_ok=True, parents=True)
log_cm = make_file_cm(log_path)
logger.info("Storing vagrant log in %s", log_path)

vagrant = Vagrant(root=vagrant_file, quiet_stdout=self.quiet, quiet_stderr=self.quiet,
out_cm=log_cm, err_cm=log_cm)
try:
vagrant.up()
except subprocess.CalledProcessError as e:
raise BuildError("Vagrant VM couldn't up launched. See output for details.")

api.env.hosts = [vagrant.user_hostname_port()]
api.env.key_filename = vagrant.keyfile()
api.env.disable_known_hosts = True # useful for when the vagrant box ip changes.
api.env.abort_exception = BuildError # raises SystemExit otherwise
api.env.shell = "/bin/sh -l -c"
if self.quiet:
api.output.everything = False
else:
Expand All @@ -127,4 +156,7 @@ def run(self, repo: str, branch: str, task: Task, git_repo: Repo, repo_path: Pat
"exception": e
})
finally:
vagrant.halt()
if self.destroy:
vagrant.destroy()
else:
vagrant.halt()
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[pytest]
addopts = --flake8 --cov arca --cov-report term-missing --cov-report xml:coverage.xml
# -s is required to test vagrant - fabric needs stdin not to be captured
addopts = -s --flake8 --cov arca --cov-report term-missing --cov-report xml:coverage.xml
flake8-max-line-length = 120
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
gitpython
pytest
dogpile.cache==0.6.4
docker
docker~=3.0
pytest-flake8
pytest-cov
pytest-mock
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def read(filename: str) -> str:
"gitpython==2.1.7",
"dogpile.cache==0.6.4",
"requests",
"docker",
"docker~=3.0",
"python-vagrant",
"fabric3"
],
Expand Down
85 changes: 85 additions & 0 deletions tests/test_vagrant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from pathlib import Path
from uuid import uuid4

import os
import pytest
from git import Repo

from arca import VagrantBackend, Arca, Task
from arca.exceptions import ArcaMisconfigured, BuildError
from common import BASE_DIR, RETURN_DJANGO_VERSION_FUNCTION


def test_validation():
""" These tests work on Travis
"""
backend = VagrantBackend()

# VagrantBackend requires `push_to_registry`
with pytest.raises(ArcaMisconfigured):
Arca(backend=backend)

# validation passes
backend = VagrantBackend(push_to_registry_name="docker.io/mikicz/arca-test")
Arca(backend=backend)

# valid different box
backend = VagrantBackend(push_to_registry_name="docker.io/mikicz/arca-test", box="hashicorp/precise64")
assert Arca(backend=backend).backend.box == "hashicorp/precise64"

# invalid box
backend = VagrantBackend(push_to_registry_name="docker.io/mikicz/arca-test", box="ubuntu rusty64\"")
with pytest.raises(ArcaMisconfigured):
Arca(backend=backend)

# valid different provider
backend = VagrantBackend(push_to_registry_name="docker.io/mikicz/arca-test", provider="vmware_fusion")
assert Arca(backend=backend).backend.provider == "vmware_fusion"

# invalid provider
backend = VagrantBackend(push_to_registry_name="docker.io/mikicz/arca-test", provider="libvirst\"")
with pytest.raises(ArcaMisconfigured):
Arca(backend=backend)


# `hashicorp/boot2docker` contains docker 1.7 - doesn't work
# `asdfkljasdf/asdfasdf` doesn't exist
@pytest.mark.parametrize("box", [None, "hashicorp/boot2docker", "asdfkljasdf/asdfasdf"])
def test_vagrant(box):
if os.environ.get("TRAVIS", False):
pytest.skip("Vagrant doesn't work on Travis")

kwargs = {}
if box:
kwargs["box"] = box
backend = VagrantBackend(verbosity=2, push_to_registry_name="docker.io/mikicz/arca-test", **kwargs)
arca = Arca(backend=backend, base_dir=BASE_DIR)
git_dir = Path("/tmp/arca/") / str(uuid4())
git_repo = Repo.init(git_dir)

filepath = git_dir / "test_file.py"
filepath.write_text(RETURN_DJANGO_VERSION_FUNCTION)
git_repo.index.add([str(filepath)])

requirements_path = git_dir / backend.requirements_location
requirements_path.parent.mkdir(exist_ok=True, parents=True)
with requirements_path.open("w") as fl:
fl.write("django==1.11.3") # Has to be unique in Arca tests.
git_repo.index.add([str(requirements_path)])

git_repo.index.commit("Initial")

task = Task(
"return_str_function",
from_imports=[("test_file", "return_str_function")]
)

repo = f"file://{git_dir}"
branch = "master"

if not box:
result = arca.run(repo, branch, task)
assert result.output == "1.11.3"
else:
with pytest.raises(BuildError): # fails because of reasons listed above
arca.run(repo, branch, task)

0 comments on commit 1760430

Please sign in to comment.