Allow adding new definitions to PKICatalog
* Detect and re-use existing Certs/Keys * Negative functional test for join with missing cert * Positive functional test to generate cert after initial construction * Extract some promenade test code into tools/g2/lib/promenade.sh * Add timestamps to tar'd up files Change-Id: Ib717785fc2c8f6cd1db1970ecdf1f5184ed40e92
This commit is contained in:
parent
066ce3e24c
commit
26e6792690
|
@ -1,7 +1,10 @@
|
|||
PKI Catalog
|
||||
===========
|
||||
|
||||
Configuration for certificate and keypair generation in the cluster.
|
||||
Configuration for certificate and keypair generation in the cluster. The
|
||||
``promenade generate-certs`` command will read all ``PKICatalog`` documents and
|
||||
either find pre-existing certificates/keys, or generate new ones based on the
|
||||
given definition.
|
||||
|
||||
|
||||
Sample Document
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
schema: promenade/PKICatalog/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: cluster-certificates-addition
|
||||
layeringDefinition:
|
||||
abstract: false
|
||||
layer: site
|
||||
data:
|
||||
certificate_authorities:
|
||||
kubernetes:
|
||||
description: CA for Kubernetes components
|
||||
certificates:
|
||||
- document_name: kubelet-n3
|
||||
common_name: system:node:n3
|
||||
hosts:
|
||||
- n3
|
||||
- 192.168.77.13
|
||||
groups:
|
||||
- system:nodes
|
||||
...
|
|
@ -48,13 +48,6 @@ data:
|
|||
- 192.168.77.12
|
||||
groups:
|
||||
- system:nodes
|
||||
- document_name: kubelet-n3
|
||||
common_name: system:node:n3
|
||||
hosts:
|
||||
- n3
|
||||
- 192.168.77.13
|
||||
groups:
|
||||
- system:nodes
|
||||
- document_name: scheduler
|
||||
description: Service certificate for Kubernetes scheduler
|
||||
common_name: system:kube-scheduler
|
||||
|
|
|
@ -94,7 +94,7 @@ class Configuration:
|
|||
'No document found matching kind=%s schema=%s name=%s' %
|
||||
(kind, schema, name))
|
||||
|
||||
def iterate(self, *, kind=None, schema=None, labels=None):
|
||||
def iterate(self, *, kind=None, schema=None, labels=None, name=None):
|
||||
if kind is not None:
|
||||
if schema is not None:
|
||||
raise AssertionError(
|
||||
|
@ -102,9 +102,14 @@ class Configuration:
|
|||
schema = 'promenade/%s/v1' % kind
|
||||
|
||||
for document in self.documents:
|
||||
if _matches_filter(document, schema=schema, labels=labels):
|
||||
if _matches_filter(
|
||||
document, schema=schema, labels=labels, name=name):
|
||||
yield document
|
||||
|
||||
def find(self, *args, **kwargs):
|
||||
for doc in self.iterate(*args, **kwargs):
|
||||
return doc
|
||||
|
||||
def extract_genesis_config(self):
|
||||
LOG.debug('Extracting genesis config.')
|
||||
documents = []
|
||||
|
@ -179,7 +184,7 @@ class Configuration:
|
|||
['/apiserver', '--apiserver-count=2', '--v=5'])
|
||||
|
||||
|
||||
def _matches_filter(document, *, schema, labels):
|
||||
def _matches_filter(document, *, schema, labels, name):
|
||||
matches = True
|
||||
if schema is not None and not document.get('schema',
|
||||
'').startswith(schema):
|
||||
|
@ -194,6 +199,10 @@ def _matches_filter(document, *, schema, labels):
|
|||
if document_labels[key] != value:
|
||||
matches = False
|
||||
|
||||
if name is not None:
|
||||
if _mg(document, 'name') != name:
|
||||
matches = False
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
|
|
|
@ -50,8 +50,9 @@ class JoinScriptsResource(BaseResource):
|
|||
design_ref,
|
||||
allow_missing_substitutions=False,
|
||||
leave_kubectl=leave_kubectl)
|
||||
except exceptions.DeckhandException as e:
|
||||
raise falcon.HTTPInternalServerError(description=str(e))
|
||||
except exceptions.DeckhandException:
|
||||
LOG.exception('Caught Deckhand render error for configuration')
|
||||
raise
|
||||
|
||||
if config.get_path('KubernetesNode:.', SENTINEL) != SENTINEL:
|
||||
raise exceptions.ExistingKubernetesNodeDocumentError(
|
||||
|
|
|
@ -222,6 +222,24 @@ class PromenadeException(Exception):
|
|||
LOG.error(self.title + (self.description or ''))
|
||||
|
||||
|
||||
class PKIError(PromenadeException):
|
||||
"""
|
||||
A parent error for PKI-related issues.
|
||||
"""
|
||||
title = 'PKI Error'
|
||||
# NOTE(mark-burnett): The API should never see these errors.
|
||||
status = falcon.HTTP_500
|
||||
|
||||
|
||||
class IncompletePKIPairError(PKIError):
|
||||
"""
|
||||
An incomplete pair (Certificate + Key or Pub + Priv) was found in cache.
|
||||
"""
|
||||
title = 'Incomplete Pair Error'
|
||||
# NOTE(mark-burnett): The API should never see these errors.
|
||||
status = falcon.HTTP_500
|
||||
|
||||
|
||||
class ApiError(PromenadeException):
|
||||
"""
|
||||
An error to handle general api errors.
|
||||
|
@ -294,6 +312,11 @@ class ValidationException(PromenadeException):
|
|||
|
||||
class DeckhandException(PromenadeException):
|
||||
title = 'Deckhand Engine Error'
|
||||
status = falcon.HTTP_400
|
||||
|
||||
|
||||
class TemplateRenderException(PromenadeException):
|
||||
title = 'Template Rendering Error Error'
|
||||
status = falcon.HTTP_500
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from . import logging, pki
|
||||
from . import exceptions, logging, pki
|
||||
import collections
|
||||
import itertools
|
||||
import os
|
||||
import yaml
|
||||
|
||||
|
@ -12,35 +14,129 @@ class Generator:
|
|||
self.config = config
|
||||
self.keys = pki.PKI()
|
||||
self.documents = []
|
||||
self.outputs = collections.defaultdict(dict)
|
||||
|
||||
@property
|
||||
def cluster_domain(self):
|
||||
return self.config['KubernetesNetwork:dns.cluster_domain']
|
||||
|
||||
def generate(self, output_dir):
|
||||
for ca_name, ca_def in self.config[
|
||||
'PKICatalog:certificate_authorities'].items():
|
||||
self.gen('ca', ca_name)
|
||||
for cert_def in ca_def.get('certificates', []):
|
||||
hosts = cert_def.get('hosts', [])
|
||||
hosts.extend(
|
||||
get_host_list(
|
||||
cert_def.get('kubernetes_service_names', [])))
|
||||
self.gen(
|
||||
'certificate',
|
||||
cert_def['document_name'],
|
||||
ca=ca_name,
|
||||
cn=cert_def['common_name'],
|
||||
hosts=hosts,
|
||||
groups=cert_def.get('groups', []))
|
||||
for keypair_def in self.config['PKICatalog:keypairs']:
|
||||
self.gen('keypair', keypair_def['name'])
|
||||
_write(output_dir, self.documents)
|
||||
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)
|
||||
|
||||
def gen(self, kind, *args, **kwargs):
|
||||
method = getattr(self.keys, 'generate_' + kind)
|
||||
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', []))
|
||||
|
||||
self.documents.extend(method(*args, **kwargs))
|
||||
for keypair_def in catalog['data'].get('keypairs', []):
|
||||
document_name = keypair_def['name']
|
||||
self.get_or_gen_keypair(document_name)
|
||||
|
||||
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):
|
||||
docs = list(
|
||||
itertools.chain.from_iterable(
|
||||
v.values() for v in self.outputs.values()))
|
||||
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(
|
||||
docs,
|
||||
stream=f,
|
||||
default_flow_style=False,
|
||||
explicit_start=True,
|
||||
indent=2)
|
||||
|
||||
|
||||
def get_host_list(service_names):
|
||||
|
@ -52,12 +148,7 @@ def get_host_list(service_names):
|
|||
return service_list
|
||||
|
||||
|
||||
def _write(output_dir, docs):
|
||||
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(
|
||||
docs,
|
||||
stream=f,
|
||||
default_flow_style=False,
|
||||
explicit_start=True,
|
||||
indent=2)
|
||||
def _extract_hosts(cert_def):
|
||||
hosts = cert_def.get('hosts', [])
|
||||
hosts.extend(get_host_list(cert_def.get('kubernetes_service_names', [])))
|
||||
return hosts
|
||||
|
|
|
@ -14,7 +14,6 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class PKI:
|
||||
def __init__(self):
|
||||
self.certificate_authorities = {}
|
||||
self._ca_config_string = None
|
||||
|
||||
@property
|
||||
|
@ -40,7 +39,6 @@ class PKI:
|
|||
files={
|
||||
'csr.json': self.csr(name=ca_name, groups=['Kubernetes']),
|
||||
})
|
||||
self.certificate_authorities[ca_name] = result
|
||||
|
||||
return (self._wrap_ca(ca_name, result['cert']),
|
||||
self._wrap_ca_key(ca_name, result['key']))
|
||||
|
@ -56,7 +54,19 @@ class PKI:
|
|||
return (self._wrap_pub_key(name, pub_result['pub.pem']),
|
||||
self._wrap_priv_key(name, priv_result['priv.pem']))
|
||||
|
||||
def generate_certificate(self, name, *, ca, cn, groups=[], hosts=[]):
|
||||
def generate_certificate(self,
|
||||
name,
|
||||
*,
|
||||
ca_cert,
|
||||
ca_key,
|
||||
cn,
|
||||
groups=None,
|
||||
hosts=None):
|
||||
if groups is None:
|
||||
groups = []
|
||||
if hosts is None:
|
||||
hosts = []
|
||||
|
||||
result = self._cfssl(
|
||||
[
|
||||
'gencert', '-ca', 'ca.pem', '-ca-key', 'ca-key.pem', '-config',
|
||||
|
@ -64,8 +74,8 @@ class PKI:
|
|||
],
|
||||
files={
|
||||
'ca-config.json': self.ca_config,
|
||||
'ca.pem': self.certificate_authorities[ca]['cert'],
|
||||
'ca-key.pem': self.certificate_authorities[ca]['key'],
|
||||
'ca.pem': ca_cert,
|
||||
'ca-key.pem': ca_key,
|
||||
'csr.json': self.csr(name=cn, groups=groups, hosts=hosts),
|
||||
})
|
||||
|
||||
|
@ -75,12 +85,17 @@ class PKI:
|
|||
def csr(self,
|
||||
*,
|
||||
name,
|
||||
groups=[],
|
||||
hosts=[],
|
||||
groups=None,
|
||||
hosts=None,
|
||||
key={
|
||||
'algo': 'rsa',
|
||||
'size': 2048
|
||||
}):
|
||||
if groups is None:
|
||||
groups = []
|
||||
if hosts is None:
|
||||
hosts = []
|
||||
|
||||
return json.dumps({
|
||||
'CN': name,
|
||||
'key': key,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from . import logging, tar_bundler
|
||||
from . import exceptions, logging, tar_bundler
|
||||
import base64
|
||||
import datetime
|
||||
import io
|
||||
|
@ -75,7 +75,13 @@ def render_template_into_bundler(*, bundler, config, destination_path,
|
|||
with open(source_path) as f:
|
||||
template = env.from_string(f.read())
|
||||
now = int(datetime.datetime.utcnow().timestamp())
|
||||
data = template.render(config=config, now=now)
|
||||
try:
|
||||
data = template.render(config=config, now=now)
|
||||
except jinja2.exceptions.TemplateRuntimeError as e:
|
||||
LOG.exception('Error rendering template (%s)' % source_path)
|
||||
raise exceptions.TemplateRenderException(
|
||||
'Error rendering template (%s): %s' % (source_path, e))
|
||||
|
||||
bundler.add(path=destination_path, data=data, mode=mode)
|
||||
|
||||
|
||||
|
@ -91,7 +97,12 @@ def render_template(config, *, template, context=None):
|
|||
env = _build_env()
|
||||
|
||||
template_obj = env.from_string(template_contents.decode('utf-8'))
|
||||
return template_obj.render(config=config, **context)
|
||||
try:
|
||||
return template_obj.render(config=config, **context)
|
||||
except jinja2.exceptions.TemplateRuntimeError as e:
|
||||
LOG.exception('Error rendering template (%s)' % template)
|
||||
raise exceptions.TemplateRenderException(
|
||||
'Error rendering template (%s): %s' % (template, e))
|
||||
|
||||
|
||||
def _build_env():
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import hashlib
|
||||
import io
|
||||
import tarfile
|
||||
import time
|
||||
|
||||
from promenade import logging
|
||||
|
||||
|
@ -25,6 +26,7 @@ class TarBundler:
|
|||
data_bytes = data
|
||||
tar_info.size = len(data_bytes)
|
||||
tar_info.mode = mode
|
||||
tar_info.mtime = int(time.time())
|
||||
|
||||
if tar_info.size > 0:
|
||||
# Ignore bandit false positive: B303:blacklist
|
||||
|
|
|
@ -4,6 +4,7 @@ export BASE_IMAGE_URL=${BASE_IMAGE_URL:-https://cloud-images.ubuntu.com/releases
|
|||
export IMAGE_PROMENADE=${IMAGE_PROMENADE:-quay.io/attcomdev/promenade:latest}
|
||||
export NGINX_DIR="${TEMP_DIR}/nginx"
|
||||
export NGINX_URL="http://192.168.77.1:7777"
|
||||
export PROMENADE_BASE_URL="http://promenade-api.ucp.svc.cluster.local"
|
||||
export PROMENADE_DEBUG=${PROMENADE_DEBUG:-0}
|
||||
export REGISTRY_DATA_DIR=${REGISTRY_DATA_DIR:-/mnt/registry}
|
||||
export VIRSH_POOL=${VIRSH_POOL:-promenade}
|
||||
|
|
|
@ -5,3 +5,59 @@ promenade_teardown_node() {
|
|||
ssh_cmd "${TARGET}" /usr/local/bin/promenade-teardown
|
||||
kubectl_cmd "${VIA}" delete node "${TARGET}"
|
||||
}
|
||||
|
||||
promenade_render_curl_url() {
|
||||
NAME=${1}
|
||||
USE_DECKHAND=${2}
|
||||
DECKHAND_REVISION=${3}
|
||||
shift 3
|
||||
LABELS=(${@})
|
||||
|
||||
LABEL_PARAMS=
|
||||
for label in "${LABELS[@]}"; do
|
||||
LABEL_PARAMS+="&labels.dynamic=${label}"
|
||||
done
|
||||
|
||||
BASE_URL="${PROMENADE_BASE_URL}/api/v1.0/join-scripts"
|
||||
if [[ ${USE_DECKHAND} == 1 ]]; then
|
||||
DESIGN_REF="design_ref=deckhand%2Bhttp://deckhand-int.ucp.svc.cluster.local:9000/api/v1.0/revisions/${DECKHAND_REVISION}/rendered-documents"
|
||||
else
|
||||
DESIGN_REF="design_ref=${NGINX_URL}/promenade.yaml"
|
||||
fi
|
||||
HOST_PARAMS="hostname=${NAME}&ip=$(config_vm_ip "${NAME}")"
|
||||
|
||||
echo "${BASE_URL}?${DESIGN_REF}&${HOST_PARAMS}&leave_kubectl=true${LABEL_PARAMS}"
|
||||
}
|
||||
|
||||
promenade_render_validate_url() {
|
||||
echo "${PROMENADE_BASE_URL}/api/v1.0/validatedesign"
|
||||
}
|
||||
|
||||
promenade_render_validate_body() {
|
||||
USE_DECKHAND=${1}
|
||||
DECKHAND_REVISION=${2}
|
||||
|
||||
if [[ ${USE_DECKHAND} == 1 ]]; then
|
||||
JSON="{\"rel\":\"design\",\"href\":\"deckhand+http://deckhand-int.ucp.svc.cluster.local:9000/api/v1.0/revisions/${DECKHAND_REVISION}/rendered-documents\",\"type\":\"application/x-yaml\"}"
|
||||
else
|
||||
JSON="{\"rel\":\"design\",\"href\":\"${NGINX_URL}/promenade.yaml\",\"type\":\"application/x-yaml\"}"
|
||||
fi
|
||||
|
||||
echo ${JSON}
|
||||
}
|
||||
|
||||
promenade_health_check() {
|
||||
VIA=${1}
|
||||
log "Checking Promenade API health"
|
||||
MAX_HEALTH_ATTEMPTS=6
|
||||
for attempt in $(seq ${MAX_HEALTH_ATTEMPTS}); do
|
||||
if ssh_cmd "${VIA}" curl -v --fail "${PROMENADE_BASE_URL}/api/v1.0/health"; then
|
||||
log "Promenade API healthy"
|
||||
break
|
||||
elif [[ $attempt == "${MAX_HEALTH_ATTEMPTS}" ]]; then
|
||||
log "Promenade health check failed, max retries (${MAX_HEALTH_ATTEMPTS}) exceeded."
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
},
|
||||
{
|
||||
"name": "Generate Certificates",
|
||||
"script": "generate-certificates.sh"
|
||||
"script": "generate-certificates.sh",
|
||||
"arguments": [
|
||||
"-x", "PKICatalog-addition.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Build Scripts",
|
||||
|
@ -40,6 +43,36 @@
|
|||
"-v", "n0",
|
||||
"-n", "n1",
|
||||
"-n", "n2",
|
||||
"-l", "calico-etcd=enabled",
|
||||
"-l", "kubernetes-apiserver=enabled",
|
||||
"-l", "kubernetes-controller-manager=enabled",
|
||||
"-l", "kubernetes-etcd=enabled",
|
||||
"-l", "kubernetes-scheduler=enabled",
|
||||
"-l", "ucp-control-plane=enabled",
|
||||
"-e", "kubernetes n0 n0 n1 n2",
|
||||
"-e", "calico n0 n0 n1 n2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Verify Join Failure",
|
||||
"script": "fail-join-node.sh",
|
||||
"arguments": [
|
||||
"-v", "n0",
|
||||
"-n", "n3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Update Generated Certs",
|
||||
"script": "generate-certificates.sh",
|
||||
"arguments": [
|
||||
"-u"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Join Final Master",
|
||||
"script": "join-nodes.sh",
|
||||
"arguments": [
|
||||
"-v", "n0",
|
||||
"-n", "n3",
|
||||
"-l", "calico-etcd=enabled",
|
||||
"-l", "coredns=enabled",
|
||||
|
|
|
@ -19,6 +19,3 @@ docker run --rm -t \
|
|||
--validators \
|
||||
-o scripts \
|
||||
config/*.yaml
|
||||
|
||||
mkdir -p "${NGINX_DIR}"
|
||||
cat "${TEMP_DIR}"/config/*.yaml > "${TEMP_DIR}/nginx/promenade.yaml"
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
source "${GATE_UTILS}"
|
||||
|
||||
while getopts "n:v:" opt; do
|
||||
case "${opt}" in
|
||||
n)
|
||||
NODE="${OPTARG}"
|
||||
;;
|
||||
v)
|
||||
VIA=${OPTARG}
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
echo "Unknown arguments specified: ${*}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="${TEMP_DIR}/join-fail-curled-scripts"
|
||||
|
||||
mkdir -p "${SCRIPT_DIR}"
|
||||
|
||||
CURL_ARGS=("-v" "--fail" "--max-time" "300")
|
||||
|
||||
promenade_health_check "${VIA}"
|
||||
|
||||
LABELS=(
|
||||
"foo=bar"
|
||||
)
|
||||
|
||||
USE_DECKHAND=0
|
||||
JOIN_CURL_URL="$(promenade_render_curl_url "${NODE}" "${USE_DECKHAND}" "" "${LABELS[@]}")"
|
||||
log "Attempting to get join script (should fail) via: ${JOIN_CURL_URL}"
|
||||
if ! ssh_cmd "${VIA}" curl "${CURL_ARGS[@]}" \
|
||||
"${JOIN_CURL_URL}" > "${SCRIPT_DIR}/join-${NODE}.sh"; then
|
||||
log "Failed to get join script"
|
||||
else
|
||||
log "No failure when fetching join script"
|
||||
exit 1
|
||||
fi
|
|
@ -9,11 +9,61 @@ mkdir -p "${OUTPUT_DIR}"
|
|||
chmod 777 "${OUTPUT_DIR}"
|
||||
OUTPUT_FILE="${OUTPUT_DIR}/combined.yaml"
|
||||
|
||||
CERTIFICATES_FILE="${OUTPUT_DIR}/certificates.yaml"
|
||||
OLD_CERTIFICATES_FILE="${OUTPUT_DIR}/certificates-old.yaml"
|
||||
|
||||
IS_UPDATE=0
|
||||
DO_EXCLUDE=0
|
||||
EXCLUDE_PATTERNS=()
|
||||
|
||||
while getopts "ux:" opt; do
|
||||
case "${opt}" in
|
||||
u)
|
||||
IS_UPDATE=1
|
||||
;;
|
||||
x)
|
||||
DO_EXCLUDE=1
|
||||
EXCLUDE_PATTERNS+=("${OPTARG}")
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
function should_include_filename() {
|
||||
FILENAME="${1}"
|
||||
if [[ ${DO_EXCLUDE} == 1 ]]; then
|
||||
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
|
||||
if echo "${FILENAME}" | grep "${pattern}" > /dev/null; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Ensure we do not duplicate configuration on update.
|
||||
rm -f "${OUTPUT_FILE}"
|
||||
|
||||
for source_dir in $(config_configuration); do
|
||||
log Copying configuration from "${source_dir}"
|
||||
cat "${WORKSPACE}/${source_dir}"/*.yaml >> "${OUTPUT_FILE}"
|
||||
for filename in ${WORKSPACE}/${source_dir}/*.yaml; do
|
||||
if should_include_filename "${filename}"; then
|
||||
log Including config from "$filename"
|
||||
cat "${filename}" >> "${OUTPUT_FILE}"
|
||||
else
|
||||
log Excluding config from "$filename"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ ${IS_UPDATE} == "1" && -e ${CERTIFICATES_FILE} ]]; then
|
||||
mv "${CERTIFICATES_FILE}" "${OLD_CERTIFICATES_FILE}"
|
||||
fi
|
||||
|
||||
log "Setting up local caches.."
|
||||
nginx_cache_and_replace_tar_urls "${OUTPUT_DIR}"/*.yaml
|
||||
registry_replace_references "${OUTPUT_DIR}"/*.yaml
|
||||
|
@ -30,3 +80,10 @@ docker run --rm -t \
|
|||
generate-certs \
|
||||
-o /target \
|
||||
"${FILES[@]}"
|
||||
|
||||
if [[ -e "${OLD_CERTIFICATES_FILE}" ]]; then
|
||||
rm -f "${OLD_CERTIFICATES_FILE}"
|
||||
fi
|
||||
|
||||
mkdir -p "${NGINX_DIR}"
|
||||
cat "${TEMP_DIR}"/config/*.yaml > "${TEMP_DIR}/nginx/promenade.yaml"
|
||||
|
|
|
@ -10,6 +10,7 @@ declare -a NODES
|
|||
|
||||
GET_KEYSTONE_TOKEN=0
|
||||
USE_DECKHAND=0
|
||||
DECKHAND_REVISION=''
|
||||
|
||||
while getopts "d:e:l:n:tv:" opt; do
|
||||
case "${opt}" in
|
||||
|
@ -46,43 +47,11 @@ if [ $# -gt 0 ]; then
|
|||
fi
|
||||
|
||||
SCRIPT_DIR="${TEMP_DIR}/curled-scripts"
|
||||
BASE_PROM_URL="http://promenade-api.ucp.svc.cluster.local"
|
||||
|
||||
echo Etcd Clusters: "${ETCD_CLUSTERS[@]}"
|
||||
echo Labels: "${LABELS[@]}"
|
||||
echo Nodes: "${NODES[@]}"
|
||||
|
||||
render_curl_url() {
|
||||
NAME=${1}
|
||||
shift
|
||||
LABELS=(${@})
|
||||
|
||||
LABEL_PARAMS=
|
||||
for label in "${LABELS[@]}"; do
|
||||
LABEL_PARAMS+="&labels.dynamic=${label}"
|
||||
done
|
||||
|
||||
BASE_URL="${BASE_PROM_URL}/api/v1.0/join-scripts"
|
||||
if [[ ${USE_DECKHAND} == 1 ]]; then
|
||||
DESIGN_REF="design_ref=deckhand%2Bhttp://deckhand-int.ucp.svc.cluster.local:9000/api/v1.0/revisions/${DECKHAND_REVISION}/rendered-documents"
|
||||
else
|
||||
DESIGN_REF="design_ref=${NGINX_URL}/promenade.yaml"
|
||||
fi
|
||||
HOST_PARAMS="hostname=${NAME}&ip=$(config_vm_ip "${NAME}")"
|
||||
|
||||
echo "${BASE_URL}?${DESIGN_REF}&${HOST_PARAMS}&leave_kubectl=true${LABEL_PARAMS}"
|
||||
}
|
||||
|
||||
render_validate_body() {
|
||||
if [[ ${USE_DECKHAND} == 1 ]]; then
|
||||
JSON="{\"rel\":\"design\",\"href\":\"deckhand+http://deckhand-int.ucp.svc.cluster.local:9000/api/v1.0/revisions/${DECKHAND_REVISION}/rendered-documents\",\"type\":\"application/x-yaml\"}"
|
||||
else
|
||||
JSON="{\"rel\":\"design\",\"href\":\"${NGINX_URL}/promenade.yaml\",\"type\":\"application/x-yaml\"}"
|
||||
fi
|
||||
|
||||
echo ${JSON}
|
||||
}
|
||||
|
||||
mkdir -p "${SCRIPT_DIR}"
|
||||
|
||||
for NAME in "${NODES[@]}"; do
|
||||
|
@ -100,23 +69,12 @@ for NAME in "${NODES[@]}"; do
|
|||
CURL_ARGS+=("-H" "X-Auth-Token: ${TOKEN}")
|
||||
fi
|
||||
|
||||
log "Checking Promenade API health"
|
||||
MAX_HEALTH_ATTEMPTS=6
|
||||
for attempt in $(seq ${MAX_HEALTH_ATTEMPTS}); do
|
||||
if ssh_cmd "${VIA}" curl -v "${CURL_ARGS[@]}" "${BASE_PROM_URL}/api/v1.0/health"; then
|
||||
log "Promenade API healthy"
|
||||
break
|
||||
elif [[ $attempt == "${MAX_HEALTH_ATTEMPTS}" ]]; then
|
||||
log "Promenade health check failed, max retries (${MAX_HEALTH_ATTEMPTS}) exceeded."
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
promenade_health_check "${VIA}"
|
||||
|
||||
log "Validating documents"
|
||||
ssh_cmd "${VIA}" curl -v "${CURL_ARGS[@]}" -X POST -H "Content-Type: application/json" -d $(render_validate_body) "${BASE_PROM_URL}/api/v1.0/validatedesign"
|
||||
ssh_cmd "${VIA}" curl -v "${CURL_ARGS[@]}" -X POST -H "Content-Type: application/json" -d "$(promenade_render_validate_body "${USE_DECKHAND}" "${DECKHAND_REVISION}")" "$(promenade_render_validate_url)"
|
||||
|
||||
JOIN_CURL_URL="$(render_curl_url "${NAME}" "${LABELS[@]}")"
|
||||
JOIN_CURL_URL="$(promenade_render_curl_url "${NAME}" "${USE_DECKHAND}" "${DECKHAND_REVISION}" "${LABELS[@]}")"
|
||||
log "Fetching join script via: ${JOIN_CURL_URL}"
|
||||
ssh_cmd "${VIA}" curl "${CURL_ARGS[@]}" \
|
||||
"${JOIN_CURL_URL}" > "${SCRIPT_DIR}/join-${NAME}.sh"
|
||||
|
|
Loading…
Reference in New Issue