32ad8a96b0
- uplifted/downgraded some python modules - fixed falcon.API deprecation - -> falcon.App - uplifted deckhand reference for python deps - fixed formatting style using yapf linter - added bindep role and bindep.txt file with required deps - fixed quai docker image publishing - re-enabled openstack-tox-py38 gate job Change-Id: I0e248182efad75630721a1291bc86a5edc79c22a
160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
from . import exceptions, logging, pki
|
|
import collections
|
|
import itertools
|
|
import os
|
|
import yaml
|
|
|
|
__all__ = ['Generator']
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Generator:
|
|
|
|
def __init__(self, config, block_strings=True):
|
|
self.config = config
|
|
self.keys = pki.PKI(block_strings=block_strings)
|
|
self.outputs = collections.defaultdict(dict)
|
|
|
|
@property
|
|
def cluster_domain(self):
|
|
return self.config['KubernetesNetwork:dns.cluster_domain']
|
|
|
|
def generate(self, output_dir=None):
|
|
for catalog in self.config.iterate(kind='PKICatalog'):
|
|
for ca_name, ca_def in catalog['data'].get(
|
|
'certificate_authorities', {}).items():
|
|
ca_cert, ca_key = self.get_or_gen_ca(ca_name)
|
|
|
|
for cert_def in ca_def.get('certificates', []):
|
|
document_name = cert_def['document_name']
|
|
cert, key = self.get_or_gen_cert(
|
|
document_name,
|
|
ca_cert=ca_cert,
|
|
ca_key=ca_key,
|
|
cn=cert_def['common_name'],
|
|
hosts=_extract_hosts(cert_def),
|
|
groups=cert_def.get('groups', []))
|
|
|
|
for keypair_def in catalog['data'].get('keypairs', []):
|
|
document_name = keypair_def['name']
|
|
self.get_or_gen_keypair(document_name)
|
|
|
|
if output_dir:
|
|
self._write(output_dir)
|
|
|
|
def get_or_gen_ca(self, document_name):
|
|
kinds = [
|
|
'CertificateAuthority',
|
|
'CertificateAuthorityKey',
|
|
]
|
|
return self._get_or_gen(self.gen_ca, kinds, document_name)
|
|
|
|
def get_or_gen_cert(self, document_name, **kwargs):
|
|
kinds = [
|
|
'Certificate',
|
|
'CertificateKey',
|
|
]
|
|
return self._get_or_gen(self.gen_cert, kinds, document_name, **kwargs)
|
|
|
|
def get_or_gen_keypair(self, document_name):
|
|
kinds = [
|
|
'PublicKey',
|
|
'PrivateKey',
|
|
]
|
|
return self._get_or_gen(self.gen_keypair, kinds, document_name)
|
|
|
|
def gen_ca(self, document_name, **kwargs):
|
|
return self.keys.generate_ca(document_name, **kwargs)
|
|
|
|
def gen_cert(self, document_name, *, ca_cert, ca_key, **kwargs):
|
|
ca_cert_data = ca_cert['data']
|
|
ca_key_data = ca_key['data']
|
|
return self.keys.generate_certificate(document_name,
|
|
ca_cert=ca_cert_data,
|
|
ca_key=ca_key_data,
|
|
**kwargs)
|
|
|
|
def gen_keypair(self, document_name):
|
|
return self.keys.generate_keypair(document_name)
|
|
|
|
def _get_or_gen(self, generator, kinds, document_name, *args, **kwargs):
|
|
docs = self._find_docs(kinds, document_name)
|
|
if not docs:
|
|
docs = generator(document_name, *args, **kwargs)
|
|
|
|
# Adding these to output should be idempotent, so we use a dict.
|
|
for doc in docs:
|
|
self.outputs[doc['schema']][doc['metadata']['name']] = doc
|
|
|
|
return docs
|
|
|
|
def _find_docs(self, kinds, document_name):
|
|
schemas = ['deckhand/%s/v1' % k for k in kinds]
|
|
docs = self._find_in_config(schemas, document_name)
|
|
if docs:
|
|
if len(docs) == len(kinds):
|
|
LOG.debug('Found docs in input config named %s, kinds: %s',
|
|
document_name, kinds)
|
|
return docs
|
|
else:
|
|
raise exceptions.IncompletePKIPairError('Incomplete set %s '
|
|
'for name: %s' %
|
|
(kinds, document_name))
|
|
|
|
else:
|
|
docs = self._find_in_outputs(schemas, document_name)
|
|
if docs:
|
|
LOG.debug('Found docs in current outputs named %s, kinds: %s',
|
|
document_name, kinds)
|
|
return docs
|
|
else:
|
|
LOG.debug('No docs existing docs named %s, kinds: %s',
|
|
document_name, kinds)
|
|
return []
|
|
|
|
def _find_in_config(self, schemas, document_name):
|
|
result = []
|
|
for schema in schemas:
|
|
doc = self.config.find(schema=schema, name=document_name)
|
|
if doc:
|
|
result.append(doc)
|
|
return result
|
|
|
|
def _find_in_outputs(self, schemas, document_name):
|
|
result = []
|
|
for schema in schemas:
|
|
if document_name in self.outputs.get(schema, {}):
|
|
result.append(self.outputs[schema][document_name])
|
|
return result
|
|
|
|
def _write(self, output_dir):
|
|
documents = self.get_documents()
|
|
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.
|
|
yaml.dump_all(documents,
|
|
stream=f,
|
|
default_flow_style=False,
|
|
explicit_start=True,
|
|
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):
|
|
service_list = []
|
|
for service in service_names:
|
|
parts = service.split('.')
|
|
for i in range(len(parts)):
|
|
service_list.append('.'.join(parts[:i + 1]))
|
|
return service_list
|
|
|
|
|
|
def _extract_hosts(cert_def):
|
|
hosts = cert_def.get('hosts', [])
|
|
hosts.extend(get_host_list(cert_def.get('kubernetes_service_names', [])))
|
|
return hosts
|