sahara/sahara/tests/unit/plugins/test_images.py

482 lines
20 KiB
Python

# Copyright (c) 2016 Red Hat, Inc.
#
# 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.
import mock
from oslo_utils import uuidutils
import yaml
from sahara import exceptions as ex
from sahara.plugins import exceptions as p_ex
from sahara.plugins import images
from sahara.tests.unit import base as b
class TestImages(b.SaharaTestCase):
def test_package_spec(self):
cls = images.SaharaPackageValidator
validator = cls.from_spec("java", {}, [])
self.assertIsInstance(validator, cls)
self.assertEqual(str(validator.packages[0]), "java")
validator = cls.from_spec({"java": {"version": "8"}}, {}, [])
self.assertIsInstance(validator, cls)
self.assertEqual(str(validator.packages[0]), "java-8")
validator = cls.from_spec(
[{"java": {"version": "8"}}, "hadoop"], {}, [])
self.assertIsInstance(validator, cls)
self.assertEqual(str(validator.packages[0]), "java-8")
self.assertEqual(str(validator.packages[1]), "hadoop")
def test_script_spec(self):
cls = images.SaharaScriptValidator
resource_roots = ['tests/unit/plugins']
validator = cls.from_spec('test_images.py', {}, resource_roots)
self.assertIsInstance(validator, cls)
self.assertEqual(validator.env_vars, ['test_only', 'distro'])
validator = cls.from_spec(
{'test_images.py': {'env_vars': ['extra-file', 'user']}},
{}, resource_roots)
self.assertIsInstance(validator, cls)
self.assertEqual(validator.env_vars,
['test_only', 'distro',
'extra-file', 'user'])
def test_all_spec(self):
cls = images.SaharaAllValidator
validator_map = images.SaharaImageValidatorBase.get_validator_map()
validator = cls.from_spec(
[{'package': {'java': {'version': '8'}}}, {'package': 'hadoop'}],
validator_map, [])
self.assertIsInstance(validator, cls)
self.assertEqual(len(validator.validators), 2)
self.assertEqual(validator.validators[0].packages[0].name, 'java')
self.assertEqual(validator.validators[1].packages[0].name, 'hadoop')
def test_any_spec(self):
cls = images.SaharaAnyValidator
validator_map = images.SaharaImageValidatorBase.get_validator_map()
validator = cls.from_spec(
[{'package': {'java': {'version': '8'}}}, {'package': 'hadoop'}],
validator_map, [])
self.assertIsInstance(validator, cls)
self.assertEqual(len(validator.validators), 2)
self.assertEqual(validator.validators[0].packages[0].name, 'java')
self.assertEqual(validator.validators[1].packages[0].name, 'hadoop')
def test_os_case_spec(self):
cls = images.SaharaOSCaseValidator
validator_map = images.SaharaImageValidatorBase.get_validator_map()
spec = [
{'redhat': [{'package': 'nfs-utils'}]},
{'debian': [{'package': 'nfs-common'}]}
]
validator = cls.from_spec(spec, validator_map, [])
self.assertIsInstance(validator, cls)
self.assertEqual(len(validator.distros), 2)
self.assertEqual(validator.distros[0].distro, 'redhat')
self.assertEqual(validator.distros[1].distro, 'debian')
redhat, debian = (
validator.distros[os].validator.validators[0].packages[0].name
for os in range(2))
self.assertEqual(redhat, 'nfs-utils')
self.assertEqual(debian, 'nfs-common')
def test_sahara_image_validator_spec(self):
cls = images.SaharaImageValidator
validator_map = images.SaharaImageValidatorBase.get_validator_map()
resource_roots = ['tests/unit/plugins']
spec = """
arguments:
java-version:
description: The version of java.
default: openjdk
required: false
choices:
- openjdk
- oracle-java
validators:
- os_case:
- redhat:
- package: nfs-utils
- debian:
- package: nfs-common
- any:
- all:
- package: java-1.8.0-openjdk-devel
- argument_set:
argument_name: java-version
value: 1.8.0
- all:
- package: java-1.7.0-openjdk-devel
- argument_set:
argument_name: java-version
value: 1.7.0
- script: test_images.py
- package:
- hadoop
- hadoop-libhdfs
- hadoop-native
- hadoop-pipes
- hadoop-sbin
- hadoop-lzo
- lzo
- lzo-devel
- hadoop-lzo-native
- argument_case:
argument_name: JAVA_VERSION
cases:
1.7.0:
- script: test_images.py
1.8.0:
- script: test_images.py
"""
spec = yaml.safe_load(spec)
validator = cls.from_spec(spec, validator_map, resource_roots)
validators = validator.validators
self.assertIsInstance(validator, cls)
self.assertEqual(len(validators), 5)
self.assertIsInstance(validators[0], images.SaharaOSCaseValidator)
self.assertIsInstance(validators[1], images.SaharaAnyValidator)
self.assertIsInstance(validators[2], images.SaharaScriptValidator)
self.assertIsInstance(validators[3], images.SaharaPackageValidator)
self.assertIsInstance(
validators[4], images.SaharaArgumentCaseValidator)
self.assertEqual(1, len(validator.arguments))
self.assertEqual(validator.arguments['java-version'].required, False)
self.assertEqual(validator.arguments['java-version'].default,
'openjdk')
self.assertEqual(validator.arguments['java-version'].description,
'The version of java.')
self.assertEqual(validator.arguments['java-version'].choices,
['openjdk', 'oracle-java'])
def test_package_validator_redhat(self):
cls = images.SaharaPackageValidator
image_arguments = {"distro": 'centos'}
packages = [cls.Package("java", "8")]
validator = images.SaharaPackageValidator(packages)
remote = mock.Mock()
validator.validate(remote, test_only=True,
image_arguments=image_arguments)
remote.execute_command.assert_called_with(
"rpm -q java-8", run_as_root=True)
image_arguments = {"distro": 'fedora'}
packages = [cls.Package("java", "8"), cls.Package("hadoop")]
validator = images.SaharaPackageValidator(packages)
remote = mock.Mock()
remote.execute_command.side_effect = (
ex.RemoteCommandException("So bad!"))
try:
validator.validate(remote, test_only=True,
image_arguments=image_arguments)
except p_ex.ImageValidationError as e:
self.assertIn("So bad!", e.message)
remote.execute_command.assert_called_with(
"rpm -q java-8 hadoop", run_as_root=True)
self.assertEqual(remote.execute_command.call_count, 1)
image_arguments = {"distro": 'redhat'}
packages = [cls.Package("java", "8"), cls.Package("hadoop")]
validator = images.SaharaPackageValidator(packages)
remote = mock.Mock()
def side_effect(call, run_as_root=False):
if "rpm" in call:
raise ex.RemoteCommandException("So bad!")
remote.execute_command.side_effect = side_effect
try:
validator.validate(remote, test_only=False,
image_arguments=image_arguments)
except p_ex.ImageValidationError as e:
self.assertIn("So bad!", e.message)
self.assertEqual(remote.execute_command.call_count, 3)
calls = [mock.call("rpm -q java-8 hadoop", run_as_root=True),
mock.call("yum install -y java-8 hadoop", run_as_root=True),
mock.call("rpm -q java-8 hadoop", run_as_root=True)]
remote.execute_command.assert_has_calls(calls)
def test_package_validator_debian(self):
cls = images.SaharaPackageValidator
image_arguments = {"distro": 'ubuntu'}
packages = [cls.Package("java", "8")]
validator = images.SaharaPackageValidator(packages)
remote = mock.Mock()
validator.validate(remote, test_only=True,
image_arguments=image_arguments)
remote.execute_command.assert_called_with(
"dpkg -s java-8", run_as_root=True)
image_arguments = {"distro": 'ubuntu'}
packages = [cls.Package("java", "8"), cls.Package("hadoop")]
validator = images.SaharaPackageValidator(packages)
remote = mock.Mock()
remote.execute_command.side_effect = (
ex.RemoteCommandException("So bad!"))
try:
validator.validate(remote, test_only=True,
image_arguments=image_arguments)
except p_ex.ImageValidationError as e:
self.assertIn("So bad!", e.message)
remote.execute_command.assert_called_with(
"dpkg -s java-8 hadoop", run_as_root=True)
self.assertEqual(remote.execute_command.call_count, 1)
image_arguments = {"distro": 'ubuntu'}
packages = [cls.Package("java", "8"), cls.Package("hadoop")]
validator = images.SaharaPackageValidator(packages)
remote = mock.Mock()
remote.execute_command.side_effect = (
ex.RemoteCommandException("So bad!"))
try:
validator.validate(remote, test_only=False,
image_arguments=image_arguments)
except p_ex.ImageValidationError as e:
self.assertIn("So bad!", e.message)
self.assertEqual(remote.execute_command.call_count, 2)
calls = [mock.call("dpkg -s java-8 hadoop", run_as_root=True),
mock.call("DEBIAN_FRONTEND=noninteractive " +
"apt-get -y install java-8 hadoop",
run_as_root=True)]
remote.execute_command.assert_has_calls(calls)
@mock.patch('oslo_utils.uuidutils.generate_uuid')
def test_script_validator(self, uuid):
hash_value = '00000000-0000-0000-0000-000000000000'
uuidutils.generate_uuid.return_value = hash_value
cls = images.SaharaScriptValidator
image_arguments = {"distro": 'centos'}
cmd = "It's dangerous to go alone. Run this."
validator = cls(cmd, env_vars=image_arguments.keys(),
output_var="distro")
remote = mock.Mock(
execute_command=mock.Mock(
return_value=(0, 'fedora')))
validator.validate(remote, test_only=False,
image_arguments=image_arguments)
call = [mock.call('chmod +x /tmp/%(hash_value)s.sh' %
{'hash_value': hash_value}, run_as_root=True),
mock.call('/tmp/%(hash_value)s.sh' %
{'hash_value': hash_value}, run_as_root=True)]
remote.execute_command.assert_has_calls(call)
self.assertEqual(image_arguments['distro'], 'fedora')
def test_any_validator(self):
cls = images.SaharaAnyValidator
class FakeValidator(images.SaharaImageValidatorBase):
def __init__(self, mock_validate):
self.mock_validate = mock_validate
def validate(self, remote, test_only=False, **kwargs):
self.mock_validate(remote, test_only=test_only, **kwargs)
# One success short circuits validation
always_tells_the_truth = FakeValidator(mock.Mock())
validator = cls([always_tells_the_truth, always_tells_the_truth])
validator.validate(None, test_only=False)
self.assertEqual(always_tells_the_truth.mock_validate.call_count, 1)
# All failures fails, and calls with test_only=True on all first
always_lies = FakeValidator(
mock.Mock(side_effect=p_ex.ImageValidationError("Oh no!")))
validator = cls([always_lies, always_lies])
try:
validator.validate(None, test_only=False)
except p_ex.ImageValidationError:
pass
self.assertEqual(always_lies.mock_validate.call_count, 4)
# But it fails after a first pass if test_only=True.
always_lies = FakeValidator(
mock.Mock(side_effect=p_ex.ImageValidationError("Oh no!")))
validator = cls([always_lies, always_lies])
try:
validator.validate(None, test_only=True)
except p_ex.ImageValidationError:
pass
self.assertEqual(always_lies.mock_validate.call_count, 2)
# One failure doesn't end iteration.
always_tells_the_truth = FakeValidator(mock.Mock())
always_lies = FakeValidator(
mock.Mock(side_effect=p_ex.ImageValidationError("Oh no!")))
validator = cls([always_lies, always_tells_the_truth])
validator.validate(None, test_only=False)
self.assertEqual(always_lies.mock_validate.call_count, 1)
self.assertEqual(always_tells_the_truth.mock_validate.call_count, 1)
def test_all_validator(self):
cls = images.SaharaAllValidator
# All pass
always_tells_the_truth = mock.Mock()
validator = cls([always_tells_the_truth, always_tells_the_truth])
validator.validate(None, test_only=False)
self.assertEqual(always_tells_the_truth.validate.call_count, 2)
always_tells_the_truth.validate.assert_called_with(
None, test_only=False, image_arguments=None)
# Second fails
always_tells_the_truth = mock.Mock()
always_lies = mock.Mock(validate=mock.Mock(
side_effect=p_ex.ImageValidationError("Boom!")))
validator = cls([always_tells_the_truth, always_lies])
try:
validator.validate(None, test_only=True)
except p_ex.ImageValidationError:
pass
self.assertEqual(always_tells_the_truth.validate.call_count, 1)
self.assertEqual(always_lies.validate.call_count, 1)
always_tells_the_truth.validate.assert_called_with(
None, test_only=True, image_arguments=None)
always_lies.validate.assert_called_with(
None, test_only=True, image_arguments=None)
# First fails
always_tells_the_truth = mock.Mock()
always_lies = mock.Mock(validate=mock.Mock(
side_effect=p_ex.ImageValidationError("Boom!")))
validator = cls([always_lies, always_tells_the_truth])
try:
validator.validate(None, test_only=True, image_arguments={})
except p_ex.ImageValidationError:
pass
self.assertEqual(always_lies.validate.call_count, 1)
always_lies.validate.assert_called_with(
None, test_only=True, image_arguments={})
self.assertEqual(always_tells_the_truth.validate.call_count, 0)
def test_os_case_validator(self):
cls = images.SaharaOSCaseValidator
Distro = images.SaharaOSCaseValidator._distro_tuple
# First match wins and short circuits iteration
centos = Distro("centos", mock.Mock())
redhat = Distro("redhat", mock.Mock())
distros = [centos, redhat]
image_arguments = {images.SaharaImageValidator.DISTRO_KEY: "centos"}
validator = cls(distros)
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(centos.validator.validate.call_count, 1)
self.assertEqual(redhat.validator.validate.call_count, 0)
centos.validator.validate.assert_called_with(
None, test_only=False, image_arguments=image_arguments)
# Families match
centos = Distro("centos", mock.Mock())
redhat = Distro("redhat", mock.Mock())
distros = [centos, redhat]
image_arguments = {images.SaharaImageValidator.DISTRO_KEY: "fedora"}
validator = cls(distros)
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(centos.validator.validate.call_count, 0)
self.assertEqual(redhat.validator.validate.call_count, 1)
redhat.validator.validate.assert_called_with(
None, test_only=False, image_arguments=image_arguments)
# Non-matches do nothing
centos = Distro("centos", mock.Mock())
redhat = Distro("redhat", mock.Mock())
distros = [centos, redhat]
image_arguments = {images.SaharaImageValidator.DISTRO_KEY: "ubuntu"}
validator = cls(distros)
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(centos.validator.validate.call_count, 0)
self.assertEqual(redhat.validator.validate.call_count, 0)
def test_sahara_argument_case_validator(self):
cls = images.SaharaArgumentCaseValidator
# Match gets called
image_arguments = {"argument": "value"}
match = mock.Mock()
nomatch = mock.Mock()
cases = {"value": match,
"another_value": nomatch}
validator = cls("argument", cases)
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(match.validate.call_count, 1)
self.assertEqual(nomatch.validate.call_count, 0)
match.validate.assert_called_with(
None, test_only=False, image_arguments=image_arguments)
# Non-matches do nothing
image_arguments = {"argument": "value"}
nomatch = mock.Mock()
cases = {"some_value": nomatch,
"another_value": nomatch}
validator = cls("argument", cases)
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(nomatch.validate.call_count, 0)
def test_sahara_argument_set_validator(self):
cls = images.SaharaArgumentSetterValidator
# Old variable is overwritten
image_arguments = {"argument": "value"}
validator = cls("argument", "new_value")
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(image_arguments["argument"], "new_value")
# New variable is set
image_arguments = {"argument": "value"}
validator = cls("another_argument", "value")
validator.validate(None, test_only=False,
image_arguments=image_arguments)
self.assertEqual(image_arguments,
{"argument": "value", "another_argument": "value"})
def test_sahara_image_validator(self):
cls = images.SaharaImageValidator
sub_validator = mock.Mock(validate=mock.Mock())
remote = mock.Mock(get_os_distrib=mock.Mock(
return_value="centos"))
validator = cls(sub_validator, {})
validator.validate(remote, test_only=False, image_arguments={})
expected_map = {images.SaharaImageValidatorBase.DISTRO_KEY: "centos"}
sub_validator.validate.assert_called_with(
remote, test_only=False, image_arguments=expected_map)
expected_map = {images.SaharaImageValidatorBase.DISTRO_KEY: "centos"}
validator.validate(remote, test_only=True, image_arguments={})
sub_validator.validate.assert_called_with(
remote, test_only=True, image_arguments=expected_map)