Skip to content

Commit

Permalink
Merge pull request #23 from whole-tale/master
Browse files Browse the repository at this point in the history
Porting fixes from master to stable
  • Loading branch information
craig-willis authored Jun 8, 2018
2 parents cac2d9f + 8761589 commit c04b65f
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 26 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ These are detailed below, but in short:
* Wildcard DNS for your domain
* [Globus Auth client ID and secret](https://auth.globus.org/v2/web/developers)
* rclone binary
* GoDaddy API integration


## OpenStack
Expand Down Expand Up @@ -74,6 +75,10 @@ token = {"access_token":"<token>","token_type":"bearer","refresh_token":"<token>

Rclone is used by the `wholetale/backup` container to backup and restore home directories and Mongo using Box.

## GoDaddy API Integration

The deployment process uses the GoDaddy API to automatically create DNS entries for non-production deployments and for wildcard certificate generation.

## Terraform variables

The deployment process uses [Terraform](https://www.terraform.io/). You'll need to [download and install Terraform for your OS](https://www.terraform.io/downloads.html). Tthis deployment process currently supports only the OpenStack provider.
Expand All @@ -88,7 +93,8 @@ The ``variables.tf`` file contains variables used during the deployment process.
* globus_client_id: Globus auth client ID
* globus_client_secret: Globus auth client secret
* docker_mtu: Docker MTU for OpenStack
* restore_url: Mongo DB restore URL
* godaddy_api_key: GoDaddy API key
* godaddy_api_secret: GoDaddy API secret

## Terraform deployment

Expand Down
36 changes: 23 additions & 13 deletions assets/traefik/traefik.tpl
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@
graceTimeOut = "10s"
debug = false
checkNewVersion = false
logLevel = "WARN"
logLevel = "INFO"

# If set to true invalid SSL certificates are accepted for backends.
# Note: This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
# Optional
# Default: false
#
#
# InsecureSkipVerify = true

defaultEntryPoints = ["http", "https"]

[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[entryPoints.https.tls]
#MinVersion = "VersionTLS12"
#CipherSuites = ["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"]
MinVersion = "VersionTLS10"
CipherSuites = ["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"]

[acme]
email = "[email protected]" # FIXME
storage = "/acme/acme.json"
entryPoint = "https"
acmeLogging = true
onDemand = true
# Enable certificate generation on frontends Host rules. This will request a certificate from Let's Encrypt for each frontend with a Host rule.
# For example, a rule Host:test1.traefik.io,test2.traefik.io will request a certificate with main domain test1.traefik.io and SAN test2.traefik.io.
#
# Optional
#
# OnHostRule = true
# caServer = "https://acme-staging.api.letsencrypt.org/directory"

[acme.httpChallenge]
entryPoint = "http"

[acme.dnsChallenge]
provider = "godaddy"
delayBeforeCheck = 0

[[acme.domains]]
main = "*.${subdomain}.${domain}"

[[acme.domains]]
main = "dashboard-${subdomain}.${domain}"

[web]
address = ":8080"


[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "${domain}"
domain = "${subdomain}.${domain}"
watch = true
exposedbydefault = true
swarmmode = true
8 changes: 8 additions & 0 deletions dns.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "null_resource" "update_dns" {
depends_on = ["openstack_compute_floatingip_associate_v2.fip_master"]

provisioner "local-exec" {
command = "docker run -v `pwd`/scripts:/scripts jfloff/alpine-python:2.7-slim -p requests -- python scripts/godaddy-update-dns.py -k ${var.godaddy_api_key} -s ${var.godaddy_api_secret} -d ${var.domain} -n ${var.subdomain} -a ${openstack_networking_floatingip_v2.swarm_master_ip.address}"
}

}
79 changes: 79 additions & 0 deletions scripts/godaddy-update-dns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/python
import argparse
import json
import requests

#
# Simple CLI to create/update DNS A record in GoDaddy
#

parser = argparse.ArgumentParser()

parser.add_argument("-k", "--key", required=True, help="Godaddy API key")
parser.add_argument("-s", "--secret", required=True, help="Godaddy API secret")
parser.add_argument("-d", "--domain", required=True, help="Domain name")
parser.add_argument("-n", "--name", required=True, help="DNS A record entry name")
parser.add_argument("-a", "--address", required=True, help="DNS A record ip address value")


args = parser.parse_args()

# Uses the records and A record enpoints
baseUrl = "https://api.godaddy.com/v1"
recordsUrl = "%s/domains/%s/records" % (baseUrl, args.domain)
recordUrl = "%s/A/*.%s" % (recordsUrl, args.name)

# Authentication requires GoDaddy API key and secret
authHeader = "sso-key %s:%s" % (args.key, args.secret)

# DNS A record
wildcard_record = [{
'data': args.address,
'name': "*." + args.name,
'ttl': 600,
'type': 'A'
}]

try:

# Get the record for this name
r = requests.get(recordUrl, headers={'Authorization': authHeader})

# Endpoint should return 200 whether record exists or not
if r.status_code == requests.codes.ok:

body = r.json()

if not body:
# If body is empty, no existing A record exists so created it using the PATCH method
print("No record found for *.%s, creating" % args.name)

r = requests.patch(recordsUrl, json=wildcard_record, headers={'Authorization': authHeader, 'accept': 'application/json'})
if r.status_code == requests.codes.ok:
print("Record successfully created")
else:
print("Error: Failed to create record: %s" % r.status_code)

else:
# If body is not empty, confirm that it differs from the specified information and update if needed
if body == wildcard_record:
print("Record found, but configuration unchanged")
else:
print("Record found for %s, updating" % args.name)
r = requests.put(recordUrl, json=wildcard_record, headers={'Authorization': authHeader, 'accept': 'application/json'})

if r.status_code == requests.codes.ok:
print("Record successfully updated")
else:
print("Error: Failed to create: %s" % r.status_code)

# Get the final record and con firm it matches what was supplied
r = requests.get(recordUrl, headers={'Authorization': authHeader})

print(json.dumps(r.json(), indent=4, sort_keys=True))

else:
r.raise_for_status()

except requests.exceptions.RequestException as e:
print(e)
10 changes: 10 additions & 0 deletions scripts/pre-setup-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ docker network create \
--opt com.docker.network.bridge.enable_ip_masquerade=true \
--opt com.docker.network.driver.mtu=${mtu} \
docker_gwbridge


# Set the maximum journal size
sudo cat << EOF > /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
EOF

sudo systemctl reload systemd-journald
sudo systemctl restart systemd-journald
3 changes: 2 additions & 1 deletion scripts/start-worker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ domain=$1
role=$2
registry_user=$3
registry_pass=$4
version=$5

image=wholetale/gwvolman:stable
image=wholetale/gwvolman:${version}

sudo umount /usr/local/lib > /dev/null 2>&1 || true
docker stop celery_worker >/dev/null 2>&1
Expand Down
17 changes: 12 additions & 5 deletions stack.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ data "template_file" "traefik" {

vars {
domain = "${var.domain}"
subdomain = "${var.subdomain}"
}
}

Expand All @@ -11,12 +12,16 @@ data "template_file" "stack" {

vars {
domain = "${var.domain}"
subdomain = "${var.subdomain}"
mtu = "${var.docker_mtu}"
version = "${var.version}"
godaddy_api_key = "${var.godaddy_api_key}"
godaddy_api_secret = "${var.godaddy_api_secret}"
}
}

resource "null_resource" "label_nodes" {
depends_on = ["null_resource.provision_slave"]
depends_on = ["null_resource.provision_slave", "null_resource.provision_fileserver"]

connection {
user = "${var.ssh_user_name}"
Expand All @@ -39,7 +44,7 @@ resource "null_resource" "label_nodes" {
}

resource "null_resource" "deploy_stack" {
depends_on = ["null_resource.label_nodes"]
depends_on = ["null_resource.label_nodes", "null_resource.provision_fileserver"]

connection {
user = "${var.ssh_user_name}"
Expand Down Expand Up @@ -86,6 +91,8 @@ resource "null_resource" "deploy_stack" {
provisioner "remote-exec" {
inline = [
"chmod 600 /home/core/wholetale/traefik/acme/acme.json",
"sed -i 's/dashboard\\.prod/dashboard/g' /home/core/wholetale/swarm-compose.yaml",
"sed -i 's/dashboard-prod/dashboard/g' /home/core/wholetale/traefik/traefik.toml",
"docker stack deploy --compose-file /home/core/wholetale/swarm-compose.yaml wt",
"docker stack deploy --compose-file /home/core/wholetale/monitoring.yaml omd"
]
Expand All @@ -94,14 +101,14 @@ resource "null_resource" "deploy_stack" {
provisioner "remote-exec" {
inline = [
"chmod +x /home/core/wholetale/init-mongo.sh",
"/home/core/wholetale/init-mongo.sh ${var.domain} ${var.globus_client_id} ${var.globus_client_secret}"
"/home/core/wholetale/init-mongo.sh ${var.subdomain}.${var.domain} ${var.globus_client_id} ${var.globus_client_secret}"
]
}

provisioner "remote-exec" {
inline = [
"chmod +x /home/core/wholetale/start-worker.sh",
"/home/core/wholetale/start-worker.sh ${var.domain} manager ${var.registry_user} ${var.registry_pass}"
"/home/core/wholetale/start-worker.sh ${var.subdomain}.${var.domain} manager ${var.registry_user} ${var.registry_pass} ${var.version}"
]
}
}
Expand Down Expand Up @@ -130,7 +137,7 @@ resource "null_resource" "start_worker" {
provisioner "remote-exec" {
inline = [
"chmod +x /home/core/wholetale/start-worker.sh",
"/home/core/wholetale/start-worker.sh ${var.domain} celery ${var.registry_user} ${var.registry_pass}"
"/home/core/wholetale/start-worker.sh ${var.subdomain}.${var.domain} celery ${var.registry_user} ${var.registry_pass} ${var.version}"
]
}
}
16 changes: 11 additions & 5 deletions stacks/core/swarm-compose.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ services:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/home/core/wholetale/traefik:/etc/traefik"
- "/home/core/wholetale/traefik/acme:/acme"
environment:
- GODADDY_API_KEY=${godaddy_api_key}
- GODADDY_API_SECRET=${godaddy_api_secret}
deploy:
replicas: 1
placement:
Expand Down Expand Up @@ -79,15 +82,18 @@ services:
constraints:
- node.labels.mongo.replica == 3
girder:
image: wholetale/girder:latest
image: wholetale/girder:${version}
networks:
- celery
- traefik-net
- mongo
volumes:
- "/mnt/homes:/tmp/wt-home-dirs"
- "/mnt/homes:/tmp/wt-tale-dirs"
deploy:
replicas: 1
labels:
- "traefik.frontend.rule=Host:girder.${domain}"
- "traefik.frontend.rule=Host:girder.${subdomain}.${domain}"
- "traefik.port=8080"
- "traefik.enable=true"
- "traefik.docker.network=wt_traefik-net"
Expand All @@ -106,16 +112,16 @@ services:
- "traefik.enable=false"

dashboard:
image: wholetale/dashboard:stable
image: wholetale/dashboard:${version}
networks:
- traefik-net
environment:
- GIRDER_API_URL=https://girder.${domain}
- GIRDER_API_URL=https://girder.${subdomain}.${domain}
deploy:
replicas: 1
labels:
- "traefik.port=80"
- "traefik.frontend.rule=Host:dashboard.${domain}"
- "traefik.frontend.rule=Host:dashboard.${subdomain}.${domain}"
- "traefik.enable=true"
- "traefik.docker.network=wt_traefik-net"
- "traefik.frontend.passHostHeader=true"
Expand Down
22 changes: 21 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ variable "image" {
}

variable "flavor" {
default = "m1.small"
default = "m1.medium"
description = "openstack flavor list : Name"
}

Expand Down Expand Up @@ -62,6 +62,11 @@ variable "domain" {
description = "Site domain name"
}

variable "subdomain" {
default = "dev"
description = "Site subdomain name"
}

variable "globus_client_id" {
default = ""
description = "Globus client ID"
Expand All @@ -86,3 +91,18 @@ variable "registry_pass" {
default = "10DSObv0Awqaa8Wz4d3K"
description = "Random password for the user used in the internal docker registry"
}

variable "godaddy_api_key" {
default = ""
description = "API key for GoDaddy DNS"
}

variable "godaddy_api_secret" {
default = ""
description = "API secret for GoDaddy DNS"
}

variable "version" {
default = "latest"
description = "Docker component versions"
}

0 comments on commit c04b65f

Please sign in to comment.