diff --git a/cca-operator/functions.sh b/cca-operator/functions.sh index 9c52d2e..229773e 100644 --- a/cca-operator/functions.sh +++ b/cca-operator/functions.sh @@ -139,3 +139,38 @@ instance_domain() { CKAN_VALUES_FILE="${1}" python3 -c 'import yaml; print(yaml.load(open("'${CKAN_VALUES_FILE}'")).get("domain", ""))' } + +add_domain_to_traefik() { + export DOMAIN="${1}" + export WITH_SANS_SSL="${2}" + export INSTANCE_ID="${3}" + ( [ -z "${DOMAIN}" ] || [ -z "${INSTANCE_ID}" ] ) && echo missing required args && return 1 + ! python3 -c 'import toml' && python3 -m pip install toml + export TEMPFILE=`mktemp` &&\ + kubectl $KUBECTL_GLOBAL_ARGS -n default get configmap etc-traefik -o yaml \ + | python3 -c ' +import sys, yaml, toml, os +conf = toml.loads(yaml.load(sys.stdin)["data"]["traefik.toml"]) +domain = os.environ["DOMAIN"] +with_sans_ssl = os.environ["WITH_SANS_SSL"] +instance_id = os.environ["INSTANCE_ID"] +if instance_id in conf["backends"] or instance_id in conf["frontends"]: + print(f"Warning! Instance ID {instance_id} already configured in traefik backends or frontends", file=sys.stderr) + exit(0) +conf["frontends"][instance_id] = {"backend": instance_id, "headers": {"SSLRedirect": True}, "passHostHeader": True, + "routes": {"route1": {"rule": f"Host:{domain}"}}} +conf["backends"][instance_id] = {"servers": {"server1": {"url": f"http://nginx.{instance_id}:8080"}}} +if with_sans_ssl == "1": + main_domain = conf["acme"]["domains"][0]["main"] + assert domain.endswith(f".{main_domain}"), f"Invalid domain {domain} - must be subdomain of the main domain {main_domain}" + conf["acme"]["domains"][0]["sans"].append(domain) +print(toml.dumps(conf)) +exit(0)' > $TEMPFILE &&\ + kubectl $KUBECTL_GLOBAL_ARGS delete configmap etc-traefik &&\ + kubectl $KUBECTL_GLOBAL_ARGS create configmap etc-traefik --from-file=traefik.toml=$TEMPFILE &&\ + rm $TEMPFILE &&\ + kubectl $KUBECTL_GLOBAL_ARGS patch deployment traefik -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"`date +'%s'`\"}}}}}" &&\ + kubectl $KUBECTL_GLOBAL_ARGS rollout status deployment traefik + [ "$?" != "0" ] && echo Failed to add domain to traefik && return 1 + return 0 +} diff --git a/cca-operator/update-instance.sh b/cca-operator/update-instance.sh index 92ffb55..3e891ff 100755 --- a/cca-operator/update-instance.sh +++ b/cca-operator/update-instance.sh @@ -12,27 +12,27 @@ echo Creating instance: ${INSTANCE_ID} INSTANCE_DOMAIN=`python3 -c ' import yaml; print(yaml.load(open("'${CKAN_VALUES_FILE}'")).get("domain", "")) -'` +' 2>/dev/null` WITH_SANS_SSL=`python3 -c ' import yaml; print("1" if yaml.load(open("'${CKAN_VALUES_FILE}'")).get("withSansSSL", False) else "0") -'` +' 2>/dev/null` REGISTER_SUBDOMAIN=`python3 -c ' import yaml; print(yaml.load(open("'${CKAN_VALUES_FILE}'")).get("registerSubdomain", "")) -'` +' 2>/dev/null` CKAN_HELM_CHART_REPO=`python3 -c ' import yaml; print(yaml.load(open("'${CKAN_VALUES_FILE}'")).get("ckanHelmChartRepo", "https://raw.githubusercontent.com/ViderumGlobal/ckan-cloud-helm/master/charts_repository")) -'` +' 2>/dev/null` CKAN_HELM_CHART_VERSION=`python3 -c ' import yaml; print(yaml.load(open("'${CKAN_VALUES_FILE}'")).get("ckanHelmChartVersion", "")) -'` +' 2>/dev/null` LOAD_BALANCER_HOSTNAME=$(kubectl $KUBECTL_GLOBAL_ARGS -n default get service traefik -o yaml \ | python3 -c 'import sys, yaml; print(yaml.load(sys.stdin)["status"]["loadBalancer"]["ingress"][0]["hostname"])' 2>/dev/null) @@ -43,68 +43,7 @@ if [ "${REGISTER_SUBDOMAIN}" != "" ]; then fi if ! [ -z "${INSTANCE_DOMAIN}" ]; then - if grep 'rule = "Host:'${INSTANCE_DOMAIN}'"' /etc/ckan-cloud/traefik-values.yaml; then - echo Skipping load balancer deployment - echo instance domain already exists in load balancer values: ${INSTANCE_DOMAIN} - else - echo Configuring load balancer for domain "${INSTANCE_DOMAIN}" - cp -f "${TRAEFIK_VALUES_FILE}" /etc/ckan-cloud/backups/traefik-values.yaml.`date +%Y-%m-%d_%H-%M` &&\ - cp -f "${TRAEFIK_VALUES_FILE}" /etc/ckan-cloud/backups/traefik-values.yaml.last - [ "$?" != "0" ] && exit 1 - - TRAEFIK_VALUES_MODIFIED_FILE=/etc/ckan-cloud/traefik-values.yaml - - if [ "${WITH_SANS_SSL}" == "1" ]; then - echo Configuring SSL - TEMPFILE=`mktemp` - python3 -c ' - import yaml, json; - traefik_values = yaml.load(open("'${TRAEFIK_VALUES_MODIFIED_FILE}'")); - def acme_domains(): - for line in traefik_values["acmeDomains"].splitlines(): - if line.startswith(" sans = ["): - line = " sans = " + json.dumps(json.loads(line.strip().split(" = ")[1]) + ["'${INSTANCE_DOMAIN}'"]) - yield line - print(yaml.dump(dict(traefik_values, acmeDomains="\n".join(acme_domains())), - default_flow_style=False)); - ' > $TEMPFILE - [ "$?" != "0" ] && exit 1 - TRAEFIK_VALUES_MODIFIED_FILE=$TEMPFILE - fi - - TEMPFILE=`mktemp` - python3 -c ' - import yaml; - traefik_values = yaml.load(open("'${TRAEFIK_VALUES_MODIFIED_FILE}'")); - traefik_values["backends"] += " \n\ - [backends.'${INSTANCE_ID}'] \n\ - [backends.'${INSTANCE_ID}'.servers.server1] \n\ - url = \"http://nginx.'${INSTANCE_NAMESPACE}'\" \n\ - "; - traefik_values["frontends"] += " \n\ - [frontends.'${INSTANCE_ID}'] \n\ - backend=\"'${INSTANCE_ID}'\" \n\ - passHostHeader = true \n\ - [frontends.'${INSTANCE_ID}'.headers] \n\ - SSLRedirect = true \n\ - [frontends.'${INSTANCE_ID}'.routes.route1] \n\ - rule = \"Host:'${INSTANCE_DOMAIN}'\" \n\ - "; - print(yaml.dump(traefik_values, default_flow_style=False)); - ' > $TEMPFILE - [ "$?" != "0" ] && exit 1 - TRAEFIK_VALUES_MODIFIED_FILE=$TEMPFILE - - mv $TRAEFIK_VALUES_MODIFIED_FILE $TRAEFIK_VALUES_FILE - - echo Deploying to kube context `kubectl $KUBECTL_GLOBAL_ARGS config current-context`, load balancer hostname: ${LOAD_BALANCER_HOSTNAME} - - helm upgrade "${TRAEFIK_HELM_RELEASE_NAME}" "${TRAEFIK_HELM_CHART_PATH}" \ - --namespace "${TRAEFIK_NAMESPACE}" -if "${TRAEFIK_VALUES_FILE}" --dry-run --debug > /dev/stderr &&\ - helm upgrade "${TRAEFIK_HELM_RELEASE_NAME}" "${TRAEFIK_HELM_CHART_PATH}" \ - --namespace "${TRAEFIK_NAMESPACE}" -if "${TRAEFIK_VALUES_FILE}" - [ "$?" != "0" ] && exit 1 - fi + ! add_domain_to_traefik "${INSTANCE_DOMAIN}" "${WITH_SANS_SSL}" "${INSTANCE_ID}" && exit 1 fi if kubectl $KUBECTL_GLOBAL_ARGS get ns "${INSTANCE_NAMESPACE}"; then diff --git a/jenkins/scripts/create_instance.sh b/jenkins/scripts/create_instance.sh index beaa58c..885e3a5 100755 --- a/jenkins/scripts/create_instance.sh +++ b/jenkins/scripts/create_instance.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash -echo "${VALUES}" | tee /dev/stdout | /etc/ckan-cloud/cca_operator.sh ./set-instance-values.sh ${INSTANCE_ID} &&\ +if ! [ -z "${VALUES}" ]; then + ! echo "${VALUES}" | tee /dev/stderr | /etc/ckan-cloud/cca_operator.sh ./set-instance-values.sh ${INSTANCE_ID} && exit 1 +fi + /etc/ckan-cloud/cca_operator.sh ./create-instance.sh ${INSTANCE_ID} diff --git a/jenkins/scripts/get_instance_values.sh b/jenkins/scripts/get_instance_values.sh new file mode 100755 index 0000000..c9904bf --- /dev/null +++ b/jenkins/scripts/get_instance_values.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +export QUIET=1 +/etc/ckan-cloud/cca_operator.sh ./get-instance-values.sh "${INSTANCE_ID}" \ + | python3 -c " +import yaml, sys; +print(yaml.dump(yaml.load(sys.stdin), default_flow_style=False, indent=2, width=99999, allow_unicode=True)) +" diff --git a/jenkins/scripts/update_instance.sh b/jenkins/scripts/update_instance.sh index 89f31b1..790b195 100755 --- a/jenkins/scripts/update_instance.sh +++ b/jenkins/scripts/update_instance.sh @@ -14,5 +14,5 @@ values = yaml.load(open("'${VALUES_TEMPFILE}'")) values.update(**yaml.load(sys.stdin)) print(yaml.dump(values, default_flow_style=False, allow_unicode=True)) ' > $TEMPFILE &&\ -cat $TEMPFILE | tee /dev/stdout | /etc/ckan-cloud/cca_operator.sh ./set-instance-values.sh ${INSTANCE_ID} &&\ +cat $TEMPFILE | tee /dev/stderr | /etc/ckan-cloud/cca_operator.sh ./set-instance-values.sh ${INSTANCE_ID} &&\ /etc/ckan-cloud/cca_operator.sh ${UPDATE_INSTANCE_COMMAND:-./update-instance.sh} ${INSTANCE_ID}