# Copyright (c) 2017-2019, Stefan Grönke
# Copyright (c) 2014-2018, iocage
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""ioc provisioning prototype."""
import typing
import urllib.parse
import libioc.errors
import libioc.Types
import libioc.helpers
import libioc.Provisioning.ix
import libioc.Provisioning.puppet
_SourceType = typing.Union[
urllib.parse.ParseResult,
libioc.Types.AbsolutePath,
]
_SourceInputType = typing.Union[_SourceType, str]
[docs]class Source(str):
_value: _SourceType
def __init__(
self,
value: _SourceInputType
) -> None:
self.value = value
@property
def value(self) -> _SourceType:
return self._value
@value.setter
def value(self, value: _SourceInputType) -> None:
if isinstance(value, libioc.Types.AbsolutePath) is True:
self._value = typing.cast(libioc.Types.AbsolutePath, value)
return
elif isinstance(value, urllib.parse.ParseResult) is True:
url = typing.cast(urllib.parse.ParseResult, value)
self.__require_valid_url(url)
self._value = url
return
elif isinstance(value, str) is False:
raise TypeError(
f"Input must be URL, AbsolutePath or str, "
"but was {type(value)}"
)
try:
self._value = libioc.Types.AbsolutePath(value)
return
except ValueError:
pass
try:
url = urllib.parse.urlparse(value)
self.__require_valid_url(url)
self._value = url
return
except ValueError:
pass
raise ValueError("Provisioning Source must be AbsolutePath or URL")
@property
def local(self) -> bool:
"""Return True when the source is local."""
return (isinstance(self.value, libioc.Types.AbsolutePath) is True)
@property
def remote(self) -> bool:
"""Return True when the source is a remote URL."""
return (self.local is False)
def __require_valid_url(self, url: urllib.parse.ParseResult) -> None:
if url.scheme not in ("https", "http", "ssh", "git"):
raise ValueError(f"Invalid Source Scheme: {url.scheme}")
def __str__(self) -> str:
"""Return the Provisioning Source as string."""
value = self.value
if isinstance(value, urllib.parse.ParseResult) is True:
return value.geturl()
else:
return str(value)
def __repr__(self) -> str:
return f"<Source '{self.__str__()}'>"
[docs]class Prototype:
jail: 'libioc.Jail.JailGenerator'
__METHOD: str
def __init__(
self,
jail: 'libioc.Jail.JailGenerator'
) -> None:
self.jail = jail
@property
def method(self) -> str:
return self.__METHOD
@property
def source(self) -> typing.Optional[Source]:
config_value = self.jail.config["provision.source"]
return None if (config_value is None) else Source(config_value)
@property
def rev(self) -> typing.Optional[str]:
config_value = self.jail.config["provision.rev"]
return None if (config_value is None) else str(Source(config_value))
[docs] def check_requirements(self) -> None:
"""Check requirements before executing the provisioner."""
if self.source is None:
raise libioc.errors.UndefinedProvisionerSource(
logger=self.jail.logger
)
if self.method is None:
raise libioc.errors.UndefinedProvisionerMethod(
logger=self.jail.logger
)
[docs]class Provisioner(Prototype):
@property
def method(self) -> str:
method = self.jail.config["provision.method"]
if method in self.__available_provisioning_modules:
return method
raise libioc.errors.InvalidProvisionerMethod(
method,
logger=self.jail.logger
)
@property
def __available_provisioning_modules(
self
) -> typing.Dict[str, Prototype]:
return dict(
ix=libioc.Provisioning.ix,
puppet=libioc.Provisioning.puppet
)
@property
def __provisioning_module(self) -> 'libioc.Provisioning.Provisioner':
"""Return the class of the currently configured provisioner."""
return self.__available_provisioning_modules[self.method]
[docs] def provision(
self
) -> typing.Generator['libioc.events.IocEvent', None, None]:
"""Run the provision method on the enabled provisioner."""
Prototype.check_requirements(self)
yield from self.__provisioning_module.provision(self)