Implement encryption for genesis/join scripts
This introduces a new document called `EncryptionPolicy` to configure this behavior. It currently only supports using symmetric encryption with `GPG`, but that should be available on all Ubuntu systems (which is what we currently support) and should also be fairly reliable. Change-Id: I06d4faa119b736773df0d8cbf0e7a23fd98edcdf Depends-On: https://review.openstack.org/#/c/602175/
This commit is contained in:
parent
24e4ebf37a
commit
8bc8c7c028
8
Makefile
8
Makefile
@ -36,7 +36,7 @@ CHARTS := $(patsubst charts/%/.,%,$(wildcard charts/*/.))
|
|||||||
all: charts lint
|
all: charts lint
|
||||||
|
|
||||||
.PHONY: tests
|
.PHONY: tests
|
||||||
tests: gate-lint
|
tests: external-deps gate-lint
|
||||||
tox
|
tox
|
||||||
|
|
||||||
.PHONY: tests-security
|
.PHONY: tests-security
|
||||||
@ -48,9 +48,13 @@ docs:
|
|||||||
tox -e docs
|
tox -e docs
|
||||||
|
|
||||||
.PHONY: tests-unit
|
.PHONY: tests-unit
|
||||||
tests-unit:
|
tests-unit: external-deps
|
||||||
tox -e py35
|
tox -e py35
|
||||||
|
|
||||||
|
.PHONY: external-deps
|
||||||
|
external-deps:
|
||||||
|
./tools/install-external-deps.sh
|
||||||
|
|
||||||
.PHONY: tests-pep8
|
.PHONY: tests-pep8
|
||||||
tests-pep8:
|
tests-pep8:
|
||||||
tox -e pep8
|
tox -e pep8
|
||||||
|
33
doc/source/configuration/encryption-policy.yaml
Normal file
33
doc/source/configuration/encryption-policy.yaml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
EncryptionPolicy
|
||||||
|
================
|
||||||
|
|
||||||
|
Encryption policy defines how encryption should be applied via Promenade. The
|
||||||
|
primary use-case for this is to encrypt ``genesis.sh`` or ``join.sh`` scripts.
|
||||||
|
|
||||||
|
Sample Document
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
schema: promenade/EncryptionPolicy/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: encryption-policy
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
scripts:
|
||||||
|
genesis:
|
||||||
|
gpg: {}
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Scripts
|
||||||
|
-------
|
||||||
|
|
||||||
|
The genesis and join scripts can be built with sensitive content encrypted.
|
||||||
|
Currently the only encryption method available is ``gpg``, which can be enabled
|
||||||
|
by setting that key to an empty dictionary.
|
@ -12,6 +12,7 @@ Details about Promenade-specific documents can be found here:
|
|||||||
:caption: Documents
|
:caption: Documents
|
||||||
|
|
||||||
docker
|
docker
|
||||||
|
encryption-policy
|
||||||
genesis
|
genesis
|
||||||
host-system
|
host-system
|
||||||
kubelet
|
kubelet
|
||||||
|
14
examples/basic/EncryptionPolicy.yaml
Normal file
14
examples/basic/EncryptionPolicy.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/EncryptionPolicy/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: encryption-policy
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
scripts:
|
||||||
|
genesis:
|
||||||
|
gpg: {}
|
||||||
|
...
|
@ -1,4 +1,4 @@
|
|||||||
from . import logging, renderer
|
from . import encryption_method, logging, renderer
|
||||||
from beaker.cache import CacheManager
|
from beaker.cache import CacheManager
|
||||||
from beaker.util import parse_cache_config_options
|
from beaker.util import parse_cache_config_options
|
||||||
|
|
||||||
@ -72,6 +72,14 @@ class Builder:
|
|||||||
_write_script(output_dir, 'validate-cluster.sh', validate_script)
|
_write_script(output_dir, 'validate-cluster.sh', validate_script)
|
||||||
|
|
||||||
def build_genesis(self, *, output_dir):
|
def build_genesis(self, *, output_dir):
|
||||||
|
script = self.build_genesis_script()
|
||||||
|
_write_script(output_dir, 'genesis.sh', script)
|
||||||
|
|
||||||
|
if self.validators:
|
||||||
|
validate_script = self._build_genesis_validate_script()
|
||||||
|
_write_script(output_dir, 'validate-genesis.sh', validate_script)
|
||||||
|
|
||||||
|
def build_genesis_script(self):
|
||||||
LOG.info('Building genesis script')
|
LOG.info('Building genesis script')
|
||||||
sub_config = self.config.extract_genesis_config()
|
sub_config = self.config.extract_genesis_config()
|
||||||
tarball = renderer.build_tarball_from_roles(
|
tarball = renderer.build_tarball_from_roles(
|
||||||
@ -79,17 +87,23 @@ class Builder:
|
|||||||
roles=['common', 'genesis'],
|
roles=['common', 'genesis'],
|
||||||
file_specs=self.file_cache.values())
|
file_specs=self.file_cache.values())
|
||||||
|
|
||||||
script = renderer.render_template(
|
(encrypted_tarball, decrypt_setup_command, decrypt_command,
|
||||||
|
decrypt_teardown_command) = _encrypt_genesis(sub_config, tarball)
|
||||||
|
|
||||||
|
return renderer.render_template(
|
||||||
sub_config,
|
sub_config,
|
||||||
template='scripts/genesis.sh',
|
template='scripts/genesis.sh',
|
||||||
context={'tarball': tarball})
|
context={
|
||||||
|
'decrypt_command': decrypt_command,
|
||||||
|
'decrypt_setup_command': decrypt_setup_command,
|
||||||
|
'decrypt_teardown_command': decrypt_teardown_command,
|
||||||
|
'encrypted_tarball': encrypted_tarball,
|
||||||
|
})
|
||||||
|
|
||||||
_write_script(output_dir, 'genesis.sh', script)
|
def _build_genesis_validate_script(self):
|
||||||
|
sub_config = self.config.extract_genesis_config()
|
||||||
if self.validators:
|
return renderer.render_template(
|
||||||
validate_script = renderer.render_template(
|
sub_config, template='scripts/validate-genesis.sh')
|
||||||
sub_config, template='scripts/validate-genesis.sh')
|
|
||||||
_write_script(output_dir, 'validate-genesis.sh', validate_script)
|
|
||||||
|
|
||||||
def build_node(self, node_document, *, output_dir):
|
def build_node(self, node_document, *, output_dir):
|
||||||
node_name = node_document['metadata']['name']
|
node_name = node_document['metadata']['name']
|
||||||
@ -112,10 +126,18 @@ class Builder:
|
|||||||
tarball = renderer.build_tarball_from_roles(
|
tarball = renderer.build_tarball_from_roles(
|
||||||
config=sub_config, roles=['common', 'join'], file_specs=file_specs)
|
config=sub_config, roles=['common', 'join'], file_specs=file_specs)
|
||||||
|
|
||||||
|
(encrypted_tarball, decrypt_setup_command, decrypt_command,
|
||||||
|
decrypt_teardown_command) = _encrypt_node(sub_config, tarball)
|
||||||
|
|
||||||
return renderer.render_template(
|
return renderer.render_template(
|
||||||
sub_config,
|
sub_config,
|
||||||
template='scripts/join.sh',
|
template='scripts/join.sh',
|
||||||
context={'tarball': tarball})
|
context={
|
||||||
|
'decrypt_command': decrypt_command,
|
||||||
|
'decrypt_setup_command': decrypt_setup_command,
|
||||||
|
'decrypt_teardown_command': decrypt_teardown_command,
|
||||||
|
'encrypted_tarball': encrypted_tarball,
|
||||||
|
})
|
||||||
|
|
||||||
def _build_node_validate_script(self, node_name):
|
def _build_node_validate_script(self, node_name):
|
||||||
sub_config = self.config.extract_node_config(node_name)
|
sub_config = self.config.extract_node_config(node_name)
|
||||||
@ -123,6 +145,24 @@ class Builder:
|
|||||||
sub_config, template='scripts/validate-join.sh')
|
sub_config, template='scripts/validate-join.sh')
|
||||||
|
|
||||||
|
|
||||||
|
def _encrypt_genesis(config, data):
|
||||||
|
return _encrypt(config.get_path('EncryptionPolicy:scripts.genesis'), data)
|
||||||
|
|
||||||
|
|
||||||
|
def _encrypt_node(config, data):
|
||||||
|
return _encrypt(config.get_path('EncryptionPolicy:scripts.join'), data)
|
||||||
|
|
||||||
|
|
||||||
|
def _encrypt(cfg_dict, data):
|
||||||
|
method = encryption_method.EncryptionMethod.from_config(cfg_dict)
|
||||||
|
encrypted_data = method.encrypt(data)
|
||||||
|
decrypt_setup_command = method.get_decrypt_setup_command()
|
||||||
|
decrypt_command = method.get_decrypt_command()
|
||||||
|
decrypt_teardown_command = method.get_decrypt_teardown_command()
|
||||||
|
return (encrypted_data, decrypt_setup_command, decrypt_command,
|
||||||
|
decrypt_teardown_command)
|
||||||
|
|
||||||
|
|
||||||
@CACHE.cache('fetch_tarball_content', expire=72 * 3600)
|
@CACHE.cache('fetch_tarball_content', expire=72 * 3600)
|
||||||
def _fetch_tar_content(url, path):
|
def _fetch_tar_content(url, path):
|
||||||
content = _fetch_tar_url(url)
|
content = _fetch_tar_url(url)
|
||||||
|
182
promenade/encryption_method.py
Normal file
182
promenade/encryption_method.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
from . import exceptions, logging
|
||||||
|
import abc
|
||||||
|
import os
|
||||||
|
# Ignore bandit false positive: B404:blacklist
|
||||||
|
# The purpose of this module is to safely encapsulate calls via fork.
|
||||||
|
import subprocess # nosec
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
__all__ = ['EncryptionMethod']
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EncryptionMethod(metaclass=abc.ABCMeta):
|
||||||
|
@abc.abstractmethod
|
||||||
|
def encrypt(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_decrypt_setup_command(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_decrypt_command(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_decrypt_teardown_command(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_config(config):
|
||||||
|
LOG.debug('Building EncryptionMethod from: %s', config)
|
||||||
|
if config:
|
||||||
|
# NOTE(mark-burnett): Relying on the schema to ensure valid
|
||||||
|
# configuration.
|
||||||
|
name = list(config.keys())[0]
|
||||||
|
kwargs = config[name]
|
||||||
|
if name == 'gpg':
|
||||||
|
return GPGEncryptionMethod(**kwargs)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('Unknown Encryption method')
|
||||||
|
else:
|
||||||
|
return NullEncryptionMethod()
|
||||||
|
|
||||||
|
def notify_user(self, message):
|
||||||
|
print('=== BEGIN NOTICE ===')
|
||||||
|
print(message)
|
||||||
|
print('=== END NOTICE ===')
|
||||||
|
|
||||||
|
|
||||||
|
class NullEncryptionMethod(EncryptionMethod):
|
||||||
|
def encrypt(self, data):
|
||||||
|
LOG.debug('Performing NOOP encryption')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_decrypt_setup_command(self):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def get_decrypt_command(self):
|
||||||
|
return 'cat'
|
||||||
|
|
||||||
|
def get_decrypt_teardown_command(self):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class GPGEncryptionMethod(EncryptionMethod):
|
||||||
|
ENCRYPTION_KEY_ENV_NAME = 'PROMENADE_ENCRYPTION_KEY'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._gpg_version = _detect_gpg_version()
|
||||||
|
|
||||||
|
def encrypt(self, data):
|
||||||
|
key = self._get_key()
|
||||||
|
return self._encrypt_data(key, data)
|
||||||
|
|
||||||
|
def get_decrypt_setup_command(self):
|
||||||
|
return '''
|
||||||
|
export DECRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY:-"NONE"}
|
||||||
|
if [[ ${PROMENADE_ENCRYPTION_KEY} = "NONE" ]]; then
|
||||||
|
read -p "Script decryption key: " -s DECRYPTION_KEY
|
||||||
|
fi
|
||||||
|
'''
|
||||||
|
|
||||||
|
def get_decrypt_command(self):
|
||||||
|
return ('/usr/bin/gpg --verbose --decrypt '
|
||||||
|
'--passphrase "${DECRYPTION_KEY}"')
|
||||||
|
|
||||||
|
def get_decrypt_teardown_command(self):
|
||||||
|
return 'unset DECRYPTION_KEY'
|
||||||
|
|
||||||
|
def _get_key(self):
|
||||||
|
key = os.environ.get(self.ENCRYPTION_KEY_ENV_NAME)
|
||||||
|
if key is None:
|
||||||
|
key = _generate_key()
|
||||||
|
self.notify_user('Copy this decryption key for use during script '
|
||||||
|
'execution:\n%s' % key)
|
||||||
|
else:
|
||||||
|
LOG.info('Using encryption key from %s',
|
||||||
|
self.ENCRYPTION_KEY_ENV_NAME)
|
||||||
|
|
||||||
|
return key
|
||||||
|
|
||||||
|
def _encrypt_data(self, key, data):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
# Ignore bandit false positive:
|
||||||
|
# B603:subprocess_without_shell_equals_true
|
||||||
|
# Here user input is allowed to be arbitrary, as it's simply input
|
||||||
|
# to the specified encryption algorithm. Regardless, we only put a
|
||||||
|
# tarball here.
|
||||||
|
p = subprocess.Popen( # nosec
|
||||||
|
[
|
||||||
|
'/usr/bin/gpg',
|
||||||
|
'--verbose',
|
||||||
|
'--symmetric',
|
||||||
|
'--homedir',
|
||||||
|
tmp,
|
||||||
|
'--passphrase',
|
||||||
|
key,
|
||||||
|
] + self._gpg_encrypt_options(),
|
||||||
|
cwd=tmp,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
try:
|
||||||
|
out, err = p.communicate(data, timeout=120)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
p.kill()
|
||||||
|
out, err = p.communicate()
|
||||||
|
|
||||||
|
if p.returncode != 0:
|
||||||
|
LOG.error('Got errors from gpg encrypt: %s', err)
|
||||||
|
raise exceptions.EncryptionException(description=str(err))
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
def _gpg_encrypt_options(self):
|
||||||
|
options = {
|
||||||
|
1: [],
|
||||||
|
2: ['--pinentry-mode', 'loopback'],
|
||||||
|
}
|
||||||
|
return options[self._gpg_version[0]]
|
||||||
|
|
||||||
|
|
||||||
|
DETECTION_PREFIX = 'gpg (GnuPG) '
|
||||||
|
|
||||||
|
|
||||||
|
def _detect_gpg_version():
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
# Ignore bandit false positive:
|
||||||
|
# B603:subprocess_without_shell_equals_true
|
||||||
|
# This method takes no input and simply queries the version of gpg.
|
||||||
|
output = subprocess.check_output( # nosec
|
||||||
|
[
|
||||||
|
'/usr/bin/gpg',
|
||||||
|
'--version',
|
||||||
|
], cwd=tmp)
|
||||||
|
lines = output.decode('utf-8').strip().splitlines()
|
||||||
|
if lines:
|
||||||
|
version = lines[0][len(DETECTION_PREFIX):]
|
||||||
|
LOG.debug('Found GPG version %s', version)
|
||||||
|
return tuple(map(int, version.split('.')[:2]))
|
||||||
|
else:
|
||||||
|
raise exceptions.GPGDetectionException()
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_key():
|
||||||
|
# Ignore bandit false positive:
|
||||||
|
# B603:subprocess_without_shell_equals_true
|
||||||
|
# This method takes no input and generates random output.
|
||||||
|
result = subprocess.run( # nosec
|
||||||
|
['/usr/bin/openssl', 'rand', '-hex', '48'],
|
||||||
|
check=True,
|
||||||
|
env={
|
||||||
|
'RANDFILE': '/tmp/rnd',
|
||||||
|
},
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
|
return result.stdout.decode().strip()
|
@ -343,6 +343,16 @@ class NodeNotFoundException(KubernetesApiError):
|
|||||||
status = falcon.HTTP_404
|
status = falcon.HTTP_404
|
||||||
|
|
||||||
|
|
||||||
|
class EncryptionException(ApiError):
|
||||||
|
title = 'Payload encryption error'
|
||||||
|
status = falcon.HTTP_500
|
||||||
|
|
||||||
|
|
||||||
|
class GPGDetectionException(ApiError):
|
||||||
|
title = 'Failed to detect GPG version'
|
||||||
|
status = falcon.HTTP_500
|
||||||
|
|
||||||
|
|
||||||
def massage_error_list(error_list, placeholder_description):
|
def massage_error_list(error_list, placeholder_description):
|
||||||
"""
|
"""
|
||||||
Returns a best-effort attempt to make a nice error list
|
Returns a best-effort attempt to make a nice error list
|
||||||
|
@ -10,17 +10,16 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Generator:
|
class Generator:
|
||||||
def __init__(self, config):
|
def __init__(self, config, block_strings=True):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.keys = pki.PKI()
|
self.keys = pki.PKI(block_strings=block_strings)
|
||||||
self.documents = []
|
|
||||||
self.outputs = collections.defaultdict(dict)
|
self.outputs = collections.defaultdict(dict)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cluster_domain(self):
|
def cluster_domain(self):
|
||||||
return self.config['KubernetesNetwork:dns.cluster_domain']
|
return self.config['KubernetesNetwork:dns.cluster_domain']
|
||||||
|
|
||||||
def generate(self, output_dir):
|
def generate(self, output_dir=None):
|
||||||
for catalog in self.config.iterate(kind='PKICatalog'):
|
for catalog in self.config.iterate(kind='PKICatalog'):
|
||||||
for ca_name, ca_def in catalog['data'].get(
|
for ca_name, ca_def in catalog['data'].get(
|
||||||
'certificate_authorities', {}).items():
|
'certificate_authorities', {}).items():
|
||||||
@ -40,7 +39,8 @@ class Generator:
|
|||||||
document_name = keypair_def['name']
|
document_name = keypair_def['name']
|
||||||
self.get_or_gen_keypair(document_name)
|
self.get_or_gen_keypair(document_name)
|
||||||
|
|
||||||
self._write(output_dir)
|
if output_dir:
|
||||||
|
self._write(output_dir)
|
||||||
|
|
||||||
def get_or_gen_ca(self, document_name):
|
def get_or_gen_ca(self, document_name):
|
||||||
kinds = [
|
kinds = [
|
||||||
@ -126,18 +126,21 @@ class Generator:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _write(self, output_dir):
|
def _write(self, output_dir):
|
||||||
docs = list(
|
documents = self.get_documents()
|
||||||
itertools.chain.from_iterable(
|
|
||||||
v.values() for v in self.outputs.values()))
|
|
||||||
with open(os.path.join(output_dir, 'certificates.yaml'), 'w') as f:
|
with open(os.path.join(output_dir, 'certificates.yaml'), 'w') as f:
|
||||||
# Don't use safe_dump_all so we can block format certificate data.
|
# Don't use safe_dump_all so we can block format certificate data.
|
||||||
yaml.dump_all(
|
yaml.dump_all(
|
||||||
docs,
|
documents,
|
||||||
stream=f,
|
stream=f,
|
||||||
default_flow_style=False,
|
default_flow_style=False,
|
||||||
explicit_start=True,
|
explicit_start=True,
|
||||||
indent=2)
|
indent=2)
|
||||||
|
|
||||||
|
def get_documents(self):
|
||||||
|
return list(
|
||||||
|
itertools.chain.from_iterable(
|
||||||
|
v.values() for v in self.outputs.values()))
|
||||||
|
|
||||||
|
|
||||||
def get_host_list(service_names):
|
def get_host_list(service_names):
|
||||||
service_list = []
|
service_list = []
|
||||||
|
@ -13,7 +13,8 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class PKI:
|
class PKI:
|
||||||
def __init__(self):
|
def __init__(self, *, block_strings=True):
|
||||||
|
self.block_strings = block_strings
|
||||||
self._ca_config_string = None
|
self._ca_config_string = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -116,9 +117,11 @@ class PKI:
|
|||||||
# Ignore bandit false positive:
|
# Ignore bandit false positive:
|
||||||
# B603:subprocess_without_shell_equals_true
|
# B603:subprocess_without_shell_equals_true
|
||||||
# This method wraps cfssl calls originating from this module.
|
# This method wraps cfssl calls originating from this module.
|
||||||
return json.loads( # nosec
|
result = subprocess.check_output( # nosec
|
||||||
subprocess.check_output(
|
['cfssl'] + command, cwd=tmp, stderr=subprocess.PIPE)
|
||||||
['cfssl'] + command, cwd=tmp, stderr=subprocess.PIPE))
|
if not isinstance(result, str):
|
||||||
|
result = result.decode('utf-8')
|
||||||
|
return json.loads(result)
|
||||||
|
|
||||||
def _openssl(self, command, *, files=None):
|
def _openssl(self, command, *, files=None):
|
||||||
if not files:
|
if not files:
|
||||||
@ -175,9 +178,15 @@ class PKI:
|
|||||||
},
|
},
|
||||||
'storagePolicy': 'cleartext',
|
'storagePolicy': 'cleartext',
|
||||||
},
|
},
|
||||||
'data': block_literal(data),
|
'data': self._block_literal(data),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _block_literal(self, data):
|
||||||
|
if self.block_strings:
|
||||||
|
return block_literal(data)
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class block_literal(str):
|
class block_literal(str):
|
||||||
pass
|
pass
|
||||||
|
33
promenade/schemas/EncryptionPolicy.yaml
Normal file
33
promenade/schemas/EncryptionPolicy.yaml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
schema: deckhand/DataSchema/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Control/v1
|
||||||
|
name: promenade/EncryptionPolicy/v1
|
||||||
|
labels:
|
||||||
|
application: promenade
|
||||||
|
data:
|
||||||
|
$schema: http://json-schema.org/schema#
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
script_encryption:
|
||||||
|
oneof:
|
||||||
|
- { $ref: '#/definitions/encryption_method_gpg' }
|
||||||
|
|
||||||
|
encryption_method_gpg:
|
||||||
|
properties:
|
||||||
|
gpg:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- gpg
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
properties:
|
||||||
|
scripts:
|
||||||
|
properties:
|
||||||
|
genesis:
|
||||||
|
$ref: '#/definitions/script_encryption'
|
||||||
|
join:
|
||||||
|
$ref: '#/definitions/script_encryption'
|
||||||
|
additionalProperties: false
|
||||||
|
...
|
@ -10,7 +10,10 @@ chmod 700 /etc/kubernetes
|
|||||||
set +x
|
set +x
|
||||||
log
|
log
|
||||||
log === Extracting prepared files ===
|
log === Extracting prepared files ===
|
||||||
echo "{{ tarball | b64enc }}" | base64 -d | tar -zxv -C / | tee /etc/promenade-manifest
|
{{ decrypt_setup_command }}
|
||||||
|
echo "{{ encrypted_tarball | b64enc }}" | base64 -d | {{ decrypt_command }} | tar -zxv -C / | tee /etc/promenade-manifest
|
||||||
|
{{ decrypt_teardown_command }}
|
||||||
|
set -x
|
||||||
|
|
||||||
# Adding apt repositories
|
# Adding apt repositories
|
||||||
#
|
#
|
||||||
|
@ -105,7 +105,7 @@ def check_schema(document, schemas=None):
|
|||||||
except jsonschema.ValidationError as e:
|
except jsonschema.ValidationError as e:
|
||||||
raise exceptions.ValidationException(str(e))
|
raise exceptions.ValidationException(str(e))
|
||||||
else:
|
else:
|
||||||
LOG.warning('Skipping validation for unknown schema: %s', schema_name)
|
LOG.debug('Skipping validation for unknown schema: %s', schema_name)
|
||||||
|
|
||||||
|
|
||||||
SCHEMAS = {}
|
SCHEMAS = {}
|
||||||
|
18
tests/unit/builder_data/simple/Docker.yaml
Normal file
18
tests/unit/builder_data/simple/Docker.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/Docker/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: docker
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
config:
|
||||||
|
insecure-registries:
|
||||||
|
- registry:5000
|
||||||
|
live-restore: true
|
||||||
|
max-concurrent-downloads: 10
|
||||||
|
oom-score-adjust: -999
|
||||||
|
storage-driver: overlay2
|
||||||
|
...
|
16
tests/unit/builder_data/simple/EncryptionPolicy.yaml
Normal file
16
tests/unit/builder_data/simple/EncryptionPolicy.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/EncryptionPolicy/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: testingpolicy
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
scripts:
|
||||||
|
genesis:
|
||||||
|
gpg: {}
|
||||||
|
join:
|
||||||
|
gpg: {}
|
||||||
|
...
|
45
tests/unit/builder_data/simple/Genesis.yaml
Normal file
45
tests/unit/builder_data/simple/Genesis.yaml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/Genesis/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: genesis
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
hostname: n0
|
||||||
|
ip: 192.168.77.10
|
||||||
|
apiserver:
|
||||||
|
command_prefix:
|
||||||
|
- /apiserver
|
||||||
|
- --authorization-mode=Node,RBAC
|
||||||
|
- --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
|
||||||
|
- --service-cluster-ip-range=10.96.0.0/16
|
||||||
|
- --endpoint-reconciler-type=lease
|
||||||
|
armada:
|
||||||
|
target_manifest: cluster-bootstrap
|
||||||
|
labels:
|
||||||
|
dynamic:
|
||||||
|
- calico-etcd=enabled
|
||||||
|
- coredns=enabled
|
||||||
|
- kubernetes-apiserver=enabled
|
||||||
|
- kubernetes-controller-manager=enabled
|
||||||
|
- kubernetes-etcd=enabled
|
||||||
|
- kubernetes-scheduler=enabled
|
||||||
|
- promenade-genesis=enabled
|
||||||
|
- ucp-control-plane=enabled
|
||||||
|
images:
|
||||||
|
armada: quay.io/airshipit/armada:master
|
||||||
|
helm:
|
||||||
|
tiller: gcr.io/kubernetes-helm/tiller:v2.9.1
|
||||||
|
kubernetes:
|
||||||
|
apiserver: gcr.io/google_containers/hyperkube-amd64:v1.10.2
|
||||||
|
controller-manager: gcr.io/google_containers/hyperkube-amd64:v1.10.2
|
||||||
|
etcd: quay.io/coreos/etcd:v3.2.14
|
||||||
|
scheduler: gcr.io/google_containers/hyperkube-amd64:v1.10.2
|
||||||
|
files:
|
||||||
|
- path: /var/lib/anchor/calico-etcd-bootstrap
|
||||||
|
content: "# placeholder for triggering calico etcd bootstrapping"
|
||||||
|
mode: 0644
|
||||||
|
...
|
86
tests/unit/builder_data/simple/HostSystem.yaml
Normal file
86
tests/unit/builder_data/simple/HostSystem.yaml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/HostSystem/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: host-system
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
files:
|
||||||
|
# NOTE(mark-burnett): A kubelet would be required for a real deployment
|
||||||
|
# (either here or via debian package; however, these unit tests don't
|
||||||
|
# attempt to actually run Kubernetes, only to construct the genesis and
|
||||||
|
# join scripts.
|
||||||
|
# - path: /opt/kubernetes/bin/kubelet
|
||||||
|
# tar_url: https://dl.k8s.io/v1.10.2/kubernetes-node-linux-amd64.tar.gz
|
||||||
|
# tar_path: kubernetes/node/bin/kubelet
|
||||||
|
# mode: 0555
|
||||||
|
- path: /etc/logrotate.d/json-logrotate
|
||||||
|
mode: 0444
|
||||||
|
content: |-
|
||||||
|
/var/lib/docker/containers/*/*-json.log
|
||||||
|
{
|
||||||
|
compress
|
||||||
|
copytruncate
|
||||||
|
create 0644 root root
|
||||||
|
daily
|
||||||
|
dateext
|
||||||
|
dateformat -%Y%m%d-%s
|
||||||
|
maxsize 10M
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
su root root
|
||||||
|
rotate 1
|
||||||
|
}
|
||||||
|
images:
|
||||||
|
haproxy: haproxy:1.8.3
|
||||||
|
helm:
|
||||||
|
helm: lachlanevenson/k8s-helm:v2.9.1
|
||||||
|
kubernetes:
|
||||||
|
kubectl: gcr.io/google_containers/hyperkube-amd64:v1.10.2
|
||||||
|
packages:
|
||||||
|
repositories:
|
||||||
|
- deb http://apt.dockerproject.org/repo ubuntu-xenial main
|
||||||
|
keys:
|
||||||
|
- |-
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQINBFWln24BEADrBl5p99uKh8+rpvqJ48u4eTtjeXAWbslJotmC/CakbNSqOb9o
|
||||||
|
ddfzRvGVeJVERt/Q/mlvEqgnyTQy+e6oEYN2Y2kqXceUhXagThnqCoxcEJ3+KM4R
|
||||||
|
mYdoe/BJ/J/6rHOjq7Omk24z2qB3RU1uAv57iY5VGw5p45uZB4C4pNNsBJXoCvPn
|
||||||
|
TGAs/7IrekFZDDgVraPx/hdiwopQ8NltSfZCyu/jPpWFK28TR8yfVlzYFwibj5WK
|
||||||
|
dHM7ZTqlA1tHIG+agyPf3Rae0jPMsHR6q+arXVwMccyOi+ULU0z8mHUJ3iEMIrpT
|
||||||
|
X+80KaN/ZjibfsBOCjcfiJSB/acn4nxQQgNZigna32velafhQivsNREFeJpzENiG
|
||||||
|
HOoyC6qVeOgKrRiKxzymj0FIMLru/iFF5pSWcBQB7PYlt8J0G80lAcPr6VCiN+4c
|
||||||
|
NKv03SdvA69dCOj79PuO9IIvQsJXsSq96HB+TeEmmL+xSdpGtGdCJHHM1fDeCqkZ
|
||||||
|
hT+RtBGQL2SEdWjxbF43oQopocT8cHvyX6Zaltn0svoGs+wX3Z/H6/8P5anog43U
|
||||||
|
65c0A+64Jj00rNDr8j31izhtQMRo892kGeQAaaxg4Pz6HnS7hRC+cOMHUU4HA7iM
|
||||||
|
zHrouAdYeTZeZEQOA7SxtCME9ZnGwe2grxPXh/U/80WJGkzLFNcTKdv+rwARAQAB
|
||||||
|
tDdEb2NrZXIgUmVsZWFzZSBUb29sIChyZWxlYXNlZG9ja2VyKSA8ZG9ja2VyQGRv
|
||||||
|
Y2tlci5jb20+iQI4BBMBAgAiBQJVpZ9uAhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIe
|
||||||
|
AQIXgAAKCRD3YiFXLFJgnbRfEAC9Uai7Rv20QIDlDogRzd+Vebg4ahyoUdj0CH+n
|
||||||
|
Ak40RIoq6G26u1e+sdgjpCa8jF6vrx+smpgd1HeJdmpahUX0XN3X9f9qU9oj9A4I
|
||||||
|
1WDalRWJh+tP5WNv2ySy6AwcP9QnjuBMRTnTK27pk1sEMg9oJHK5p+ts8hlSC4Sl
|
||||||
|
uyMKH5NMVy9c+A9yqq9NF6M6d6/ehKfBFFLG9BX+XLBATvf1ZemGVHQusCQebTGv
|
||||||
|
0C0V9yqtdPdRWVIEhHxyNHATaVYOafTj/EF0lDxLl6zDT6trRV5n9F1VCEh4Aal8
|
||||||
|
L5MxVPcIZVO7NHT2EkQgn8CvWjV3oKl2GopZF8V4XdJRl90U/WDv/6cmfI08GkzD
|
||||||
|
YBHhS8ULWRFwGKobsSTyIvnbk4NtKdnTGyTJCQ8+6i52s+C54PiNgfj2ieNn6oOR
|
||||||
|
7d+bNCcG1CdOYY+ZXVOcsjl73UYvtJrO0Rl/NpYERkZ5d/tzw4jZ6FCXgggA/Zxc
|
||||||
|
jk6Y1ZvIm8Mt8wLRFH9Nww+FVsCtaCXJLP8DlJLASMD9rl5QS9Ku3u7ZNrr5HWXP
|
||||||
|
HXITX660jglyshch6CWeiUATqjIAzkEQom/kEnOrvJAtkypRJ59vYQOedZ1sFVEL
|
||||||
|
MXg2UCkD/FwojfnVtjzYaTCeGwFQeqzHmM241iuOmBYPeyTY5veF49aBJA1gEJOQ
|
||||||
|
TvBR8Q==
|
||||||
|
=Fm3p
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
additional:
|
||||||
|
- curl
|
||||||
|
- jq
|
||||||
|
required:
|
||||||
|
docker: docker-engine=1.13.1-0~ubuntu-xenial
|
||||||
|
socat: socat=1.7.3.1-1
|
||||||
|
validation:
|
||||||
|
pod_logs:
|
||||||
|
image: busybox:1.28.3
|
||||||
|
...
|
21
tests/unit/builder_data/simple/Kubelet.yaml
Normal file
21
tests/unit/builder_data/simple/Kubelet.yaml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/Kubelet/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: kubelet
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
arguments:
|
||||||
|
- --cni-bin-dir=/opt/cni/bin
|
||||||
|
- --cni-conf-dir=/etc/cni/net.d
|
||||||
|
- --eviction-max-pod-grace-period=-1
|
||||||
|
- --network-plugin=cni
|
||||||
|
- --node-status-update-frequency=5s
|
||||||
|
- --serialize-image-pulls=false
|
||||||
|
- --v=5
|
||||||
|
images:
|
||||||
|
pause: gcr.io/google_containers/pause-amd64:3.0
|
||||||
|
...
|
43
tests/unit/builder_data/simple/KubernetesNetwork.yaml
Normal file
43
tests/unit/builder_data/simple/KubernetesNetwork.yaml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/KubernetesNetwork/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: kubernetes-network
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
dns:
|
||||||
|
cluster_domain: cluster.local
|
||||||
|
service_ip: 10.96.0.10
|
||||||
|
bootstrap_validation_checks:
|
||||||
|
- calico-etcd.kube-system.svc.cluster.local
|
||||||
|
- google.com
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
- kubernetes.default.svc.cluster.local
|
||||||
|
upstream_servers:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 8.8.4.4
|
||||||
|
|
||||||
|
kubernetes:
|
||||||
|
apiserver_port: 6443
|
||||||
|
haproxy_port: 6553
|
||||||
|
pod_cidr: 10.97.0.0/16
|
||||||
|
service_cidr: 10.96.0.0/16
|
||||||
|
service_ip: 10.96.0.1
|
||||||
|
|
||||||
|
etcd:
|
||||||
|
container_port: 2379
|
||||||
|
haproxy_port: 2378
|
||||||
|
|
||||||
|
hosts_entries:
|
||||||
|
- ip: 192.168.77.1
|
||||||
|
names:
|
||||||
|
- registry
|
||||||
|
|
||||||
|
# proxy:
|
||||||
|
# url: http://proxy.example.com:8080
|
||||||
|
# additional_no_proxy:
|
||||||
|
# - 10.0.1.1
|
||||||
|
...
|
31
tests/unit/builder_data/simple/KubernetesNode.yaml
Normal file
31
tests/unit/builder_data/simple/KubernetesNode.yaml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/KubernetesNode/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: n1
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
hostname: n1
|
||||||
|
ip: 192.169.77.11
|
||||||
|
join_ip: 192.169.77.10
|
||||||
|
labels:
|
||||||
|
dynamic:
|
||||||
|
- calico-etcd=enabled
|
||||||
|
- ceph-mds=enabled
|
||||||
|
- ceph-mon=enabled
|
||||||
|
- ceph-osd=enabled
|
||||||
|
- ceph-rgw=enabled
|
||||||
|
- ceph-mgr=enabled
|
||||||
|
- coredns=enabled
|
||||||
|
- kubernetes-apiserver=enabled
|
||||||
|
- kubernetes-controller-manager=enabled
|
||||||
|
- kubernetes-etcd=enabled
|
||||||
|
- kubernetes-scheduler=enabled
|
||||||
|
- openstack-compute-node=enabled
|
||||||
|
- openstack-control-plane=enabled
|
||||||
|
- openvswitch=enabled
|
||||||
|
- ucp-control-plane=enabled
|
||||||
|
...
|
11
tests/unit/builder_data/simple/LayeringPolicy.yaml
Normal file
11
tests/unit/builder_data/simple/LayeringPolicy.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
schema: deckhand/LayeringPolicy/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Control/v1
|
||||||
|
name: layering-policy
|
||||||
|
data:
|
||||||
|
layerOrder:
|
||||||
|
- global
|
||||||
|
- type
|
||||||
|
- site
|
||||||
|
...
|
167
tests/unit/builder_data/simple/PKICatalog.yaml
Normal file
167
tests/unit/builder_data/simple/PKICatalog.yaml
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
---
|
||||||
|
schema: promenade/PKICatalog/v1
|
||||||
|
metadata:
|
||||||
|
schema: metadata/Document/v1
|
||||||
|
name: cluster-certificates
|
||||||
|
layeringDefinition:
|
||||||
|
abstract: false
|
||||||
|
layer: site
|
||||||
|
storagePolicy: cleartext
|
||||||
|
data:
|
||||||
|
certificate_authorities:
|
||||||
|
kubernetes:
|
||||||
|
description: CA for Kubernetes components
|
||||||
|
certificates:
|
||||||
|
- document_name: apiserver
|
||||||
|
description: Service certificate for Kubernetes apiserver
|
||||||
|
common_name: apiserver
|
||||||
|
hosts:
|
||||||
|
- localhost
|
||||||
|
- 127.0.0.1
|
||||||
|
- 10.96.0.1
|
||||||
|
kubernetes_service_names:
|
||||||
|
- kubernetes.default.svc.cluster.local
|
||||||
|
- document_name: kubelet-genesis
|
||||||
|
common_name: system:node:n0
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
groups:
|
||||||
|
- system:nodes
|
||||||
|
- document_name: kubelet-n0
|
||||||
|
common_name: system:node:n0
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
groups:
|
||||||
|
- system:nodes
|
||||||
|
- document_name: kubelet-n1
|
||||||
|
common_name: system:node:n1
|
||||||
|
hosts:
|
||||||
|
- n1
|
||||||
|
- 192.168.77.11
|
||||||
|
groups:
|
||||||
|
- system:nodes
|
||||||
|
- document_name: scheduler
|
||||||
|
description: Service certificate for Kubernetes scheduler
|
||||||
|
common_name: system:kube-scheduler
|
||||||
|
- document_name: controller-manager
|
||||||
|
description: certificate for controller-manager
|
||||||
|
common_name: system:kube-controller-manager
|
||||||
|
- document_name: admin
|
||||||
|
common_name: admin
|
||||||
|
groups:
|
||||||
|
- system:masters
|
||||||
|
- document_name: armada
|
||||||
|
common_name: armada
|
||||||
|
groups:
|
||||||
|
- system:masters
|
||||||
|
kubernetes-etcd:
|
||||||
|
description: Certificates for Kubernetes's etcd servers
|
||||||
|
certificates:
|
||||||
|
- document_name: apiserver-etcd
|
||||||
|
description: etcd client certificate for use by Kubernetes apiserver
|
||||||
|
common_name: apiserver
|
||||||
|
# NOTE(mark-burnett): hosts not required for client certificates
|
||||||
|
- document_name: kubernetes-etcd-anchor
|
||||||
|
description: anchor
|
||||||
|
common_name: anchor
|
||||||
|
- document_name: kubernetes-etcd-genesis
|
||||||
|
common_name: kubernetes-etcd-genesis
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
- document_name: kubernetes-etcd-n0
|
||||||
|
common_name: kubernetes-etcd-n0
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
- document_name: kubernetes-etcd-n1
|
||||||
|
common_name: kubernetes-etcd-n1
|
||||||
|
hosts:
|
||||||
|
- n1
|
||||||
|
- 192.168.77.11
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
kubernetes-etcd-peer:
|
||||||
|
certificates:
|
||||||
|
- document_name: kubernetes-etcd-genesis-peer
|
||||||
|
common_name: kubernetes-etcd-genesis-peer
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
- document_name: kubernetes-etcd-n0-peer
|
||||||
|
common_name: kubernetes-etcd-n0-peer
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
- document_name: kubernetes-etcd-n1-peer
|
||||||
|
common_name: kubernetes-etcd-n1-peer
|
||||||
|
hosts:
|
||||||
|
- n1
|
||||||
|
- 192.168.77.11
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- kubernetes-etcd.kube-system.svc.cluster.local
|
||||||
|
calico-etcd:
|
||||||
|
description: Certificates for Calico etcd client traffic
|
||||||
|
certificates:
|
||||||
|
- document_name: calico-etcd-anchor
|
||||||
|
description: anchor
|
||||||
|
common_name: anchor
|
||||||
|
- document_name: calico-etcd-n0
|
||||||
|
common_name: calico-etcd-n0
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- 10.96.232.136
|
||||||
|
- document_name: calico-etcd-n1
|
||||||
|
common_name: calico-etcd-n1
|
||||||
|
hosts:
|
||||||
|
- n1
|
||||||
|
- 192.168.77.11
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- 10.96.232.136
|
||||||
|
- document_name: calico-node
|
||||||
|
common_name: calcico-node
|
||||||
|
calico-etcd-peer:
|
||||||
|
description: Certificates for Calico etcd clients
|
||||||
|
certificates:
|
||||||
|
- document_name: calico-etcd-n0-peer
|
||||||
|
common_name: calico-etcd-n0-peer
|
||||||
|
hosts:
|
||||||
|
- n0
|
||||||
|
- 192.168.77.10
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- 10.96.232.136
|
||||||
|
- document_name: calico-etcd-n1-peer
|
||||||
|
common_name: calico-etcd-n1-peer
|
||||||
|
hosts:
|
||||||
|
- n1
|
||||||
|
- 192.168.77.11
|
||||||
|
- 127.0.0.1
|
||||||
|
- localhost
|
||||||
|
- 10.96.232.136
|
||||||
|
- document_name: calico-node-peer
|
||||||
|
common_name: calcico-node-peer
|
||||||
|
keypairs:
|
||||||
|
- name: service-account
|
||||||
|
description: Service account signing key for use by Kubernetes controller-manager.
|
||||||
|
...
|
1061
tests/unit/builder_data/simple/armada-resources.yaml
Normal file
1061
tests/unit/builder_data/simple/armada-resources.yaml
Normal file
File diff suppressed because it is too large
Load Diff
57
tests/unit/test_builder.py
Normal file
57
tests/unit/test_builder.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from promenade import builder, generator, config, encryption_method
|
||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def load_full_config(dirname):
|
||||||
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
search_dir = os.path.join(this_dir, 'builder_data', dirname)
|
||||||
|
streams = []
|
||||||
|
for filename in os.listdir(search_dir):
|
||||||
|
stream = open(os.path.join(search_dir, filename))
|
||||||
|
streams.append(stream)
|
||||||
|
|
||||||
|
raw_config = config.Configuration.from_streams(
|
||||||
|
allow_missing_substitutions=True,
|
||||||
|
debug=True,
|
||||||
|
streams=streams,
|
||||||
|
substitute=True,
|
||||||
|
validate=False,
|
||||||
|
)
|
||||||
|
g = generator.Generator(raw_config, block_strings=False)
|
||||||
|
g.generate()
|
||||||
|
|
||||||
|
documents = copy.deepcopy(raw_config.documents)
|
||||||
|
documents.extend(copy.deepcopy(g.get_documents()))
|
||||||
|
|
||||||
|
return config.Configuration(
|
||||||
|
allow_missing_substitutions=False,
|
||||||
|
debug=True,
|
||||||
|
documents=documents,
|
||||||
|
substitute=True,
|
||||||
|
validate=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_simple():
|
||||||
|
b = builder.Builder(load_full_config('simple'))
|
||||||
|
genesis_script = b.build_genesis_script()
|
||||||
|
assert len(genesis_script) > 0
|
||||||
|
|
||||||
|
n1_join_script = b.build_node_script('n1')
|
||||||
|
assert len(n1_join_script) > 0
|
@ -6,6 +6,7 @@ export NGINX_DIR="${TEMP_DIR}/nginx"
|
|||||||
export NGINX_URL="http://192.168.77.1:7777"
|
export NGINX_URL="http://192.168.77.1:7777"
|
||||||
export PROMENADE_BASE_URL="http://promenade-api.ucp.svc.cluster.local"
|
export PROMENADE_BASE_URL="http://promenade-api.ucp.svc.cluster.local"
|
||||||
export PROMENADE_DEBUG=${PROMENADE_DEBUG:-0}
|
export PROMENADE_DEBUG=${PROMENADE_DEBUG:-0}
|
||||||
|
export PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY:-testkey}
|
||||||
export REGISTRY_DATA_DIR=${REGISTRY_DATA_DIR:-/mnt/registry}
|
export REGISTRY_DATA_DIR=${REGISTRY_DATA_DIR:-/mnt/registry}
|
||||||
export VIRSH_POOL=${VIRSH_POOL:-promenade}
|
export VIRSH_POOL=${VIRSH_POOL:-promenade}
|
||||||
export VIRSH_POOL_PATH=${VIRSH_POOL_PATH:-/var/lib/libvirt/promenade}
|
export VIRSH_POOL_PATH=${VIRSH_POOL_PATH:-/var/lib/libvirt/promenade}
|
||||||
|
@ -13,6 +13,7 @@ docker run --rm -t \
|
|||||||
-w /target \
|
-w /target \
|
||||||
-v "${TEMP_DIR}:/target" \
|
-v "${TEMP_DIR}:/target" \
|
||||||
-e "PROMENADE_DEBUG=${PROMENADE_DEBUG}" \
|
-e "PROMENADE_DEBUG=${PROMENADE_DEBUG}" \
|
||||||
|
-e "PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY}" \
|
||||||
"${IMAGE_PROMENADE}" \
|
"${IMAGE_PROMENADE}" \
|
||||||
promenade \
|
promenade \
|
||||||
build-all \
|
build-all \
|
||||||
|
@ -7,7 +7,7 @@ source "${GATE_UTILS}"
|
|||||||
rsync_cmd "${TEMP_DIR}/scripts"/*genesis* "${GENESIS_NAME}:/root/promenade/"
|
rsync_cmd "${TEMP_DIR}/scripts"/*genesis* "${GENESIS_NAME}:/root/promenade/"
|
||||||
|
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
ssh_cmd "${GENESIS_NAME}" /root/promenade/genesis.sh 2>&1 | tee -a "${LOG_FILE}"
|
ssh_cmd "${GENESIS_NAME}" env "PROMENADE_ENCRYPTION_KEY=${PROMENADE_ENCRYPTION_KEY}" /root/promenade/genesis.sh 2>&1 | tee -a "${LOG_FILE}"
|
||||||
ssh_cmd "${GENESIS_NAME}" /root/promenade/validate-genesis.sh 2>&1 | tee -a "${LOG_FILE}"
|
ssh_cmd "${GENESIS_NAME}" /root/promenade/validate-genesis.sh 2>&1 | tee -a "${LOG_FILE}"
|
||||||
set +o pipefail
|
set +o pipefail
|
||||||
|
|
||||||
|
16
tools/install-external-deps.sh
Executable file
16
tools/install-external-deps.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Installs external dependencies required for basic testing
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
CFSSL_URL=${CFSSL_URL:-https://pkg.cfssl.org/R1.2/cfssl_linux-amd64}
|
||||||
|
|
||||||
|
if [[ ! $(which cfssl) ]]; then
|
||||||
|
TMP_DIR=$(mktemp -d)
|
||||||
|
pushd "${TMP_DIR}"
|
||||||
|
curl -Lo cfssl "${CFSSL_URL}"
|
||||||
|
chmod 755 cfssl
|
||||||
|
sudo mv cfssl /usr/local/bin/
|
||||||
|
popd
|
||||||
|
rm -rf "${TMP_DIR}"
|
||||||
|
fi
|
@ -12,7 +12,7 @@ done
|
|||||||
|
|
||||||
if [[ -x $(which shellcheck) ]]; then
|
if [[ -x $(which shellcheck) ]]; then
|
||||||
echo Checking shell scripts..
|
echo Checking shell scripts..
|
||||||
shellcheck -s bash -e SC2029 "${WORKSPACE}"/tools/cleanup.sh "${WORKSPACE}"/tools/*gate*.sh "${WORKSPACE}"/tools/g2/stages/* "${WORKSPACE}"/tools/g2/lib/*
|
shellcheck -s bash -e SC2029 "${WORKSPACE}"/tools/cleanup.sh "${WORKSPACE}"/tools/*gate*.sh "${WORKSPACE}"/tools/g2/stages/* "${WORKSPACE}"/tools/g2/lib/* "${WORKSPACE}"/tools/install-external-deps.sh
|
||||||
else
|
else
|
||||||
echo No shellcheck executable found. Please, install it.
|
echo No shellcheck executable found. Please, install it.
|
||||||
exit 1
|
exit 1
|
||||||
|
Loading…
Reference in New Issue
Block a user