cdist-backup/cdist/test/config/__init__.py
2020-11-11 15:05:04 +01:00

311 lines
10 KiB
Python

# -*- coding: utf-8 -*-
#
# 2010-2017 Steven Armstrong (steven-cdist at armstrong.cc)
# 2012-2015 Nico Schottelius (nico-cdist at schottelius.org)
# 2014 Daniel Heule (hda at sfs.biz)
#
# This file is part of cdist.
#
# cdist 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 3 of the License, or
# (at your option) any later version.
#
# cdist 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 cdist. If not, see <http://www.gnu.org/licenses/>.
#
#
import os
import shutil
from cdist import test
from cdist import core
import cdist
import cdist.config
import cdist.core.cdist_type
import cdist.core.cdist_object
import os.path as op
my_dir = op.abspath(op.dirname(__file__))
fixtures = op.join(my_dir, 'fixtures')
type_base_path = op.join(fixtures, 'type')
add_conf_dir = op.join(fixtures, 'conf')
expected_object_names = sorted([
'__first/man',
'__second/on-the',
'__third/moon'])
class CdistObjectErrorContext:
def __init__(self, original_error):
self.original_error = original_error
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
if exc_value.original_error:
raise exc_value.original_error
class ConfigRunTestCase(test.CdistTestCase):
def setUp(self):
# Change env for context
self.orig_environ = os.environ
os.environ = os.environ.copy()
self.temp_dir = self.mkdtemp()
self.local_dir = os.path.join(self.temp_dir, "local")
self.hostdir = cdist.str_hash(self.target_host[0])
self.host_base_path = os.path.join(self.local_dir, self.hostdir)
os.makedirs(self.host_base_path)
self.local = cdist.exec.local.Local(
target_host=self.target_host,
target_host_tags=self.target_host_tags,
base_root_path=self.host_base_path,
host_dir_name=self.hostdir)
# Setup test objects
self.object_base_path = op.join(self.temp_dir, 'object')
self.objects = []
for cdist_object_name in expected_object_names:
cdist_type, cdist_object_id = cdist_object_name.split("/", 1)
cdist_object = core.CdistObject(core.CdistType(type_base_path,
cdist_type),
self.object_base_path,
self.local.object_marker_name,
cdist_object_id)
cdist_object.create()
self.objects.append(cdist_object)
self.object_index = dict((o.name, o) for o in self.objects)
self.object_names = [o.name for o in self.objects]
self.remote_dir = os.path.join(self.temp_dir, "remote")
os.mkdir(self.remote_dir)
self.remote = cdist.exec.remote.Remote(
target_host=self.target_host,
remote_copy=self.remote_copy,
remote_exec=self.remote_exec,
base_path=self.remote_dir,
stdout_base_path=self.local.stdout_base_path,
stderr_base_path=self.local.stderr_base_path)
self.local.object_path = self.object_base_path
self.local.type_path = type_base_path
self.config = cdist.config.Config(self.local, self.remote)
def tearDown(self):
for o in self.objects:
o.requirements = []
o.state = ""
os.environ = self.orig_environ
shutil.rmtree(self.temp_dir)
def assertRaisesCdistObjectError(self, original_error, callable_obj):
"""
Test if a raised CdistObjectError was caused by the given
original_error.
"""
with self.assertRaises(original_error):
try:
callable_obj()
except cdist.CdistObjectError as e:
if e.original_error:
raise e.original_error
else:
raise
def test_dependency_resolution(self):
first = self.object_index['__first/man']
second = self.object_index['__second/on-the']
third = self.object_index['__third/moon']
first.requirements = [second.name]
second.requirements = [third.name]
# First run:
# solves first and maybe second (depending on the order in the set)
self.config.iterate_once()
self.assertTrue(third.state == third.STATE_DONE)
self.config.iterate_once()
self.assertTrue(second.state == second.STATE_DONE)
try:
self.config.iterate_once()
except cdist.Error:
# Allow failing, because the third run may or may not be
# unecessary already,
# depending on the order of the objects
pass
self.assertTrue(first.state == first.STATE_DONE)
def test_unresolvable_requirements(self):
"""Ensure an exception is thrown for unresolvable depedencies"""
# Create to objects depending on each other - no solution possible
first = self.object_index['__first/man']
second = self.object_index['__second/on-the']
first.requirements = [second.name]
second.requirements = [first.name]
self.assertRaisesCdistObjectError(
cdist.UnresolvableRequirementsError,
self.config.iterate_until_finished)
def test_missing_requirements(self):
"""Throw an error if requiring something non-existing"""
first = self.object_index['__first/man']
first.requirements = ['__first/not/exist']
self.assertRaisesCdistObjectError(
cdist.UnresolvableRequirementsError,
self.config.iterate_until_finished)
def test_requirement_broken_type(self):
"""Unknown type should be detected in the resolving process"""
first = self.object_index['__first/man']
first.requirements = ['__nosuchtype/not/exist']
self.assertRaisesCdistObjectError(
cdist.core.cdist_type.InvalidTypeError,
self.config.iterate_until_finished)
def test_requirement_singleton_where_no_singleton(self):
"""Missing object id should be detected in the resolving process"""
first = self.object_index['__first/man']
first.requirements = ['__first']
self.assertRaisesCdistObjectError(
cdist.core.cdist_object.MissingObjectIdError,
self.config.iterate_until_finished)
def test_dryrun(self):
"""Test if the dryrun option is working like expected"""
drylocal = cdist.exec.local.Local(
target_host=self.target_host,
target_host_tags=self.target_host_tags,
base_root_path=self.host_base_path,
host_dir_name=self.hostdir,
# exec_path can not derivated from sys.argv in case of unittest
exec_path=os.path.abspath(os.path.join(
my_dir, '../../../bin/cdist')),
initial_manifest=os.path.join(fixtures,
'manifest/dryrun_manifest'),
add_conf_dirs=[fixtures])
dryrun = cdist.config.Config(drylocal, self.remote, dry_run=True)
dryrun.run()
# if we are here, dryrun works like expected
def test_deps_resolver(self):
"""Test to show dependency resolver warning message."""
local = cdist.exec.local.Local(
target_host=self.target_host,
target_host_tags=self.target_host_tags,
base_root_path=self.host_base_path,
host_dir_name=self.hostdir,
exec_path=os.path.abspath(os.path.join(
my_dir, '../../../bin/cdist')),
initial_manifest=os.path.join(
fixtures, 'manifest/init-deps-resolver'),
add_conf_dirs=[fixtures])
# dry_run is ok for dependency testing
config = cdist.config.Config(local, self.remote, dry_run=True)
config.run()
def test_graph_check_cycle_empty(self):
graph = {}
has_cycle, path = cdist.config.graph_check_cycle(graph)
self.assertFalse(has_cycle)
def test_graph_check_cycle_1(self):
#
# a -> b -> c
# |
# +--> d -> e
graph = {
'a': ['b', ],
'b': ['c', 'd', ],
'd': ['e', ],
}
has_cycle, path = cdist.config.graph_check_cycle(graph)
self.assertFalse(has_cycle)
def test_graph_check_cycle_2(self):
#
# a -> b -> c
# /\ |
# \ |
# +-------+
graph = {
'a': ['b', ],
'b': ['c', ],
'c': ['a', ],
}
has_cycle, path = cdist.config.graph_check_cycle(graph)
self.assertTrue(has_cycle)
self.assertGreater(path.count(path[-1]), 1)
def test_graph_check_cycle_3(self):
#
# a -> b -> c
# \ \
# \ +--> g
# \ /\
# \ /|
# +-> d -> e |
# \ |
# + --> f
#
# h -> i --> j
# | /\ |
# \/ | \/
# n m <- k
graph = {
'a': ['b', 'd', ],
'b': ['c', ],
'c': ['g', ],
'd': ['e', 'f', ],
'e': ['g', ],
'f': ['g', ],
'h': ['i', 'n', ],
'i': ['j', ],
'j': ['k', ],
'k': ['m', ],
'm': ['i', ],
}
has_cycle, path = cdist.config.graph_check_cycle(graph)
self.assertTrue(has_cycle)
self.assertGreater(path.count(path[-1]), 1)
# Currently the resolving code will simply detect that this object does
# not exist. It should probably check if the type is a singleton as well
# - but maybe only in the emulator - to be discussed.
#
# def test_requirement_no_singleton_where_singleton(self):
# """Missing object id should be detected in the resolving process"""
# first = self.object_index['__first/man']
# first.requirements = ['__singleton_test/foo']
# with self.assertRaises(cdist.core.?????):
# self.config.iterate_until_finished()
if __name__ == "__main__":
import unittest
unittest.main()