Skip to content

Commit

Permalink
Merge branch 'release/v0.6.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Suszyński Krzysztof committed Nov 8, 2017
2 parents d16a061 + e454c23 commit 61c9c0b
Show file tree
Hide file tree
Showing 18 changed files with 217 additions and 10 deletions.
2 changes: 1 addition & 1 deletion puppeter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Type

# The version of the app
__version__ = '0.5.3'
__version__ = '0.6.0'
__program__ = 'puppeter'


Expand Down
5 changes: 5 additions & 0 deletions puppeter/domain/gateway/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def configurers(self, installer):
if installer is not None:
configurers.extend(self._provide_install_configurers(installer))
configurers.extend(self._puppet_cert_issue(installer))
configurers.extend(self._puppet_conf(installer))
configurers.extend(self._puppet_services(installer))
return tuple(configurers)

Expand All @@ -25,3 +26,7 @@ def _puppet_cert_issue(self, installer):
@abstractmethod
def _puppet_services(self, installer):
pass

@abstractmethod
def _puppet_conf(self, installer):
pass
2 changes: 2 additions & 0 deletions puppeter/domain/gateway/installer.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ class InstallerGateway:
def _provide_install_configurers(self, installer: Installer) -> Sequence[Configurer]: ...
def _puppet_cert_issue(self, installer: Installer) -> Sequence[Configurer]: ...
def _puppet_services(self, installer: Installer) -> Sequence[Configurer]: ...
def _puppet_conf(self, installer: Installer) -> Sequence[Configurer]: ...

34 changes: 30 additions & 4 deletions puppeter/domain/model/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from puppeter.container import Named
from puppeter.domain.model.gemrequirement import GemRequirement
from puppeter.domain.model.withoptions import WithOptions
from puppeter.util.dict import merge


class Mode(Enum):
Expand All @@ -18,18 +19,20 @@ class Mode(Enum):
class Installer(WithOptions):
def __init__(self):
self.__mode = Mode.Agent # type: Mode
self.__puppetconf = PuppetConf() # type: PuppetConf

def raw_options(self):
# noinspection PyUnresolvedReferences
installer_type = self.bean_name()
return {
return merge({
'mode': self.__mode.name,
'type': installer_type
}
}, self.__puppetconf.raw_options())

def read_raw_options(self, options):
try:
self.__mode = Mode[options['mode']]
self.__puppetconf.read_raw_options(options)
except KeyError:
pass

Expand All @@ -42,6 +45,10 @@ def is_after_4x(self):
# type: () -> bool
pass

def puppetconf(self):
# type: () -> PuppetConf
return self.__puppetconf


@Named('gem')
class RubygemsInstaller(Installer):
Expand Down Expand Up @@ -97,7 +104,6 @@ def to_s(self):


class JavaMemorySpec:

def __init__(self, num, scale=MemScale.MEGA):
self.__num = int(num) # type: int
self.__scale = scale # type: MemScale
Expand Down Expand Up @@ -129,7 +135,6 @@ def __eq__(self, other):


class JavaMemory(WithOptions):

def __init__(self, heap_maximum=None, heap_minimum=None, metaspace_maximum=None):
self.__heap_maximum = heap_maximum # type: JavaMemorySpec
if heap_minimum is not None:
Expand Down Expand Up @@ -217,6 +222,27 @@ def are_set(self):
return len(self.__args) > 0


class PuppetConf(WithOptions, dict):
def __init__(self, options=None):
if options is None:
options = {}
super(PuppetConf, self).__init__(options)

def read_raw_options(self, options):
try:
opts = options['puppet.conf']
self.clear()
self.update(opts)
except KeyError:
pass

def raw_options(self):
if len(self) > 0:
return {'puppet.conf': self}
else:
return {}


class After4xCollectionInstaller(CollectionInstaller):
def __init__(self):
CollectionInstaller.__init__(self)
Expand Down
1 change: 1 addition & 0 deletions puppeter/persistence/gateway/csr.pp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
file { "${settings::confdir}/csr_attributes.yaml":
ensure => 'file',
mode => '0640',
group => 'puppet',
content => '@{content}'
}
5 changes: 5 additions & 0 deletions puppeter/persistence/gateway/installer/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ def _puppet_cert_issue(self, installer):
return tuple(container.get_all_with_name_starting_with(Configurer,
'puppet.cert.issue',
installer=installer))

def _puppet_conf(self, installer):
return tuple(container.get_all_with_name_starting_with(Configurer,
'puppet.conf',
installer=installer))
3 changes: 2 additions & 1 deletion puppeter/persistence/gateway/installer/redhat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def _provide_install_configurers(self, installer):
configurers = [self.__provide_install_configurer(installer)]
return tuple(configurers)

def __provide_install_configurer(self, installer):
@staticmethod
def __provide_install_configurer(installer):
name = installer.bean_name()
if name == 'gem':
return container.get_named(Configurer, 'gem', installer=installer)
Expand Down
2 changes: 2 additions & 0 deletions puppeter/persistence/service/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from puppeter.domain.facter import Facter
from puppeter.domain.model.osfacts import OperatingSystem, OsFamily, \
OperatingSystemRelease, OperatingSystemCodename, Docker
from puppeter.persistence.service.puppetconf import PuppetConfConfigurer
from puppeter.persistence.service.puppetserver import PuppetServerServiceStarterConfigurer, \
PuppetServerJvmArgsConfigurer

Expand All @@ -22,3 +23,4 @@
container.bind(CommandsCollector, CommandsCollectorImpl)
container.bind(Configurer, PuppetServerServiceStarterConfigurer)
container.bind(Configurer, PuppetServerJvmArgsConfigurer)
container.bind(Configurer, PuppetConfConfigurer)
6 changes: 6 additions & 0 deletions puppeter/persistence/service/puppetconf.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
augeas { 'puppet.conf':
context => "/files${settings::confdir}/puppet.conf",
changes => [
@{settings}
],
}
61 changes: 61 additions & 0 deletions puppeter/persistence/service/puppetconf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import collections

from six import iteritems
from typing import Sequence, Tuple

from puppeter.container import Named
from puppeter.domain.model.configurer import Configurer, ScriptFormat
from puppeter.domain.model.installer import PuppetConf
from puppeter.domain.model.ordered import Order


@Named('puppet.conf')
@Order(900)
class PuppetConfConfigurer(Configurer):

def __init__(self, installer):
self.__puppetconf = installer.puppetconf() # type: PuppetConf

def produce_commands(self):
# type: () -> Sequence[str]
collector = self._collector()
augeas = self.__puppetconf_as_augeas()
augeas_pp = ',\n '.join(list(map(lambda cmd: '"{cmd}"'.format(cmd=cmd), augeas)))
collector.collect_from_template(
description="Configuring Puppet configuration file (puppet.conf)",
format=ScriptFormat.PUPPET,
template='puppetconf.pp',
mapping={
'settings': augeas_pp
}
)
return collector.lines()

def __puppetconf_as_augeas(self):
# type: () -> Sequence[str]
flat = self.__flatten(self.__puppetconf)
return list(map(PuppetConfConfigurer.__augeaize, iteritems(flat)))

@staticmethod
def __augeaize(tup):
# type: (Tuple[str, str]) -> str
key, value = tup
try:
safe = {
str: lambda x: "'{str}'".format(str=x),
bool: lambda x: 'true' if x else 'false',
}[value.__class__](value)
except KeyError:
safe = value
return "set {key} {value}".format(key=key, value=safe)

@staticmethod
def __flatten(d, parent_key='', sep='/'):
items = []
for k, v in iteritems(d):
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, collections.MutableMapping):
items.extend(PuppetConfConfigurer.__flatten(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
Empty file added puppeter/util/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions puppeter/util/dict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def merge(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
"""
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
24 changes: 24 additions & 0 deletions tests/domain/model/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,27 @@ def test_pc3x_is_after4x():

# then
assert installer.is_after_4x() is False


def test_pc4x_puppetconf_read():
# given
from puppeter.domain.model.installer import Installer
from puppeter import container
installer = container.get_named(Installer, 'pc4x') # type: After4xCollectionInstaller

# when
installer.read_raw_options({
'mode': 'Agent',
'puppet.conf': {
'main': {
'server': 'puppet.acme.internal',
'noop': True
}
}
})
raw = installer.raw_options()

# then
assert raw['puppet.conf']['main']['server'] == 'puppet.acme.internal'
assert installer.puppetconf()['main']['server'] == 'puppet.acme.internal'
assert installer.puppetconf()['main']['noop'] is True
23 changes: 23 additions & 0 deletions tests/persistence/gateway/example-answers-node.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
fqdn: front-01.acme.internal
installer:
mode: Agent
type: pc4x
puppet.conf:
main:
server: puppet.acme.internal
timesync:
type: ntpd
servers:
- ts1.acme.internal
- ts2.acme.internal
- ts3.acme.internal
- ts4.acme.internal
csr-attributes:
pp_project: acme
pp_role: frontend
pp_zone: app
pp_environment: stage
pp_datacenter: dc2
pp_region: public


3 changes: 3 additions & 0 deletions tests/persistence/gateway/example-answers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ fqdn: puppet.acme.internal
installer:
mode: Server
type: pc4x
puppet.conf:
main:
server: puppet.acme.internal
puppetserver:
jvm:
args:
Expand Down
Empty file.
39 changes: 39 additions & 0 deletions tests/persistence/service/test_puppetconf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from puppeter.domain.model import Collection4xInstaller
from puppeter.persistence.service import PuppetConfConfigurer


def test_puppetconf():
# given
installer = Collection4xInstaller()
installer.read_raw_options({
'mode': 'Agent',
'puppet.conf': {
'main': {
'server': 'puppet.acme.internal'
},
'agent': {
'noop': True
}
}
})
configurer = PuppetConfConfigurer(installer)

# when
commands = configurer.produce_commands()

# then
assert configurer.bean_name() == 'puppet.conf'
assert configurer.order() == 900
assert found("set main/server 'puppet.acme.internal'").inside(commands)
assert found("set agent/noop true").inside(commands)


def found(st):
class Found:
def __init__(self):
pass

@staticmethod
def inside(lst):
return any(st in s for s in lst)
return Found
8 changes: 4 additions & 4 deletions tests/presentation/test_unattendedapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def test_unattendedapp_onlyinstaller_with_verbosity(tmpdir, capsys):
' type: pc4x\n'
' mode: Server\n')
answers = open(str(tmp))
options = Options(dotdict(dict(answers=answers, verbose=2, execute=False)))
options = Options(DotDict(answers=answers, verbose=2, execute=False))
app = UnattendedApp(options)

# when
Expand All @@ -28,13 +28,13 @@ def test_unattendedapp_onlyinstaller_with_verbosity(tmpdir, capsys):
assert 'INFO: Installation commands will be generated based on answers file' in err


class dotdict(dict):
class DotDict(dict):
def __getattr__(self, name):
return self[name]


def restr(str):
return re.compile('\s*%s' % re.escape(str), re.MULTILINE)
def restr(st):
return re.compile('\s*%s' % re.escape(st), re.MULTILINE)


def setup_function():
Expand Down

0 comments on commit 61c9c0b

Please sign in to comment.