puppet module unit test coverage

Also refactored puppet module

Co-Authored-By: Dmitry Guryanov <dguryanov@mirantis.com>

Change-Id: I36ff6d0cd5fe67e9a14c7c8a5940606cc0594e6d
Partial-Bug: #1365391
This commit is contained in:
Valentin Kaplov 2016-02-09 17:52:22 +03:00 committed by Dmitry Guryanov
parent b14b7a69a8
commit 23b5bb288b
3 changed files with 128 additions and 28 deletions

View File

@ -13,44 +13,58 @@
# under the License.
import logging
import six
from fuelmenu.common.utils import execute
from fuelmenu.common import utils
from fuelmenu import consts
def _to_string(value):
if isinstance(value, bool):
return '{0},'.format(str(value).lower())
return '"{0}",'.format(value)
def puppetApply(classes):
#name should be a string
#params should be a dict or list of dicts
'''Runs puppet apply -e "classname {'name': params}".'''
"""Runs puppet apply
:param classes: list of {'type': 'name': 'params':}. name must be a string
:type classes: dict or list of dicts
"""
log = logging
log.info("Puppet start")
command = ["puppet", "apply", "-d", "-v", "--logdest",
"/var/log/puppet/fuelmenu-puppet.log"]
input = []
# TODO(mattymo): Convert puppet resource types to consts
for cls in classes:
if cls['type'] == "literal":
input.append(cls["name"])
continue
elif cls['type'] == "resource":
input.extend([cls["class"], "{", '"%s":' % cls["name"]])
elif cls['type'] == "class":
input.extend(["class", "{", '"%s":' % cls["class"]])
else:
log.error("Invalid type %s" % cls['type'])
return False
#Build params
for key, value in cls["params"].iteritems():
if type(value) == bool:
input.extend([key, "=>", '%s,' % str(value).lower()])
else:
input.extend([key, "=>", '"%s",' % value])
input.append('}')
log.debug(' '.join(command))
log.debug(' '.join(input))
code, out, err = execute(command, stdin=' '.join(input))
if code != 0:
log.error("Exit code: {0}. Error: {1} Stdout: {1}".format(
code, err, out))
puppet_type_handlers = {
consts.PUPPET_TYPE_LITERAL: lambda item: [item['name']],
consts.PUPPET_TYPE_RESOURCE: lambda item: [
item["class"], "{", '"{0}":'.format(item["name"])],
consts.PUPPET_TYPE_CLASS: lambda item: [
"class", "{", '"{0}":'.format(item["class"])]
}
cmd_input = list()
for cls in classes:
if cls['type'] not in puppet_type_handlers:
log.error("Invalid type %s", cls['type'])
return False
cmd_input.extend(puppet_type_handlers[cls['type']](cls))
if cls['type'] == consts.PUPPET_TYPE_LITERAL:
continue
#Build params
for key, value in six.iteritems(cls["params"]):
cmd_input.extend([key, "=>", _to_string(value)])
cmd_input.append('}')
stdin = ' '.join(cmd_input)
log.debug(' '.join(command))
log.debug(stdin)
code, out, err = utils.execute(command, stdin=stdin)
if code != 0:
log.error("Exit code: %d. Error: %s Stdout: %s",
code, err, out)
return False

View File

@ -23,3 +23,7 @@ RELEASE_FILE = "/etc/fuel_release"
PRE_DEPLOYMENT_MODE = "pre"
POST_DEPLOYMENT_MODE = "post"
PUPPET_TYPE_LITERAL = "literal"
PUPPET_TYPE_RESOURCE = "resource"
PUPPET_TYPE_CLASS = "class"

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Mirantis, 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
import unittest
from fuelmenu.common import puppet
@mock.patch('fuelmenu.common.puppet.logging')
@mock.patch('fuelmenu.common.puppet.utils.execute',
return_value=(0, 'Success', 0))
class TestPuppetApply(unittest.TestCase):
def setUp(self):
super(TestPuppetApply, self).setUp()
self.command = ["puppet", "apply", "-d", "-v", "--logdest",
"/var/log/puppet/fuelmenu-puppet.log"]
self.input = (
'literal_1 class_1 { "resource_1": resource_k1 => "Resource_v1", }'
' class { "class_1": class_k3 => true, }'
)
self.classes = [
{
'type': 'literal',
'name': 'literal_1',
'params': {
'literal_k1': 'Literal_v1',
}
},
{
'type': 'resource',
'name': 'resource_1',
'class': 'class_1',
'params': {
'resource_k1': 'Resource_v1',
}
},
{
'type': 'class',
'class': 'class_1',
'params': {
'class_k3': True
}
}
]
def test_puppet_apply(self, m_execute, m_log):
self.assertEqual(puppet.puppetApply(self.classes), None)
m_execute.assert_called_once_with(self.command, stdin=self.input)
m_log.info.assert_called_once_with('Puppet start')
self.assertFalse(m_log.error.called)
def test_incorrect_type(self, m_execute, m_log):
self.classes.append({
'type': 'incorrect',
'name': 'incorrect name',
'class': 'class_2',
'params': {}
})
self.assertEqual(puppet.puppetApply(self.classes), False)
self.assertFalse(m_execute.called)
m_log.error.assert_called_once_with('Invalid type %s', 'incorrect')
def test_execute_failure(self, m_execute, m_log):
res = (1, 'Fail', 5)
m_execute.return_value = res
self.assertEqual(puppet.puppetApply(self.classes), False)
m_log.error.assert_called_once_with(
'Exit code: %d. Error: %s Stdout: %s', res[0], res[2], res[1])