From 23ecd16e95ce9b642e0265036639c18e2222a80f Mon Sep 17 00:00:00 2001 From: DLX Date: Fri, 3 Mar 2017 13:02:36 +1300 Subject: [PATCH 001/149] Remove Azure Docs --- azure/research.md | 61 ----------------------------------- azure/research.org | 80 ---------------------------------------------- 2 files changed, 141 deletions(-) delete mode 100644 azure/research.md delete mode 100644 azure/research.org diff --git a/azure/research.md b/azure/research.md deleted file mode 100644 index 4bf6085..0000000 --- a/azure/research.md +++ /dev/null @@ -1,61 +0,0 @@ -- [Azure Equivalence for Existing EC2](#sec-1) - - [Locating the Equivalent CentOS Image](#sec-1-1) -- [Summary](#sec-2) - -The [Terraform Azure Provider](https://www.terraform.io/docs/providers/azure/) needs a publishsettings file - -# Azure Equivalence for Existing EC2 - -## Locating the Equivalent CentOS Image - -Currently the cncf/demo [uses the latest CentOS7 AMI](https://github.com/cncf/demo/commit/62b4ee750cea96ac18d9998cebed36660b3d5864#diff-165521d9e758a5a743ea42c6fd528156R10), so let's go find that at Azure. - -```shell -az vm image list -o table -``` - -| Offer | Publisher | Sku | Urn | UrnAlias | Version | -| ------------- | ---------------------- | ------------------ | -------------------------------------------------------------- | ------------------- | --------- | -| WindowsServer | MicrosoftWindowsServer | 2016-Datacenter | MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest | Win2016Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-R2-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest | Win2012R2Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2008-R2-SP1 | MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest | Win2008R2SP1 | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest | Win2012Datacenter | latest | -| UbuntuServer | Canonical | 14.04.4-LTS | Canonical:UbuntuServer:14.04.4-LTS:latest | UbuntuLTS | latest | -| CentOS | OpenLogic | 7.2 | OpenLogic:CentOS:7.2:latest | CentOS | latest | -| openSUSE | SUSE | 13.2 | SUSE:openSUSE:13.2:latest | openSUSE | latest | -| RHEL | RedHat | 7.2 | RedHat:RHEL:7.2:latest | RHEL | latest | -| SLES | SUSE | 12-SP1 | SUSE:SLES:12-SP1:latest | SLES | latest | -| Debian | credativ | 8 | credativ:Debian:8:latest | Debian | latest | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:latest | CoreOS | latest | - -We are looking for CentOS, and looks like the default Publisher is OpenLogic. Let's see what SKU's they have in westus. - -```shell -az vm image list-skus -o table --publisher OpenLogic --location westus --offer CentOS -``` - -| Location | Name | -| ---------- | ------ | -| westus | 6.5 | -| westus | 6.6 | -| westus | 6.7 | -| westus | 6.8 | -| westus | 7.0 | -| westus | 7.1 | -| westus | 7.2 | -| westus | 7.2n | -| westus | 7.3 | - -I prefer not to use 'latest' anywhere, let's grab the precise version of CentOS 7.3. - -```shell -az vm image list --offer centos -o table --publisher openlogic --sku 7.3 --all -``` - -| Offer | Publisher | Sku | Urn | Version | -| ------- | ----------- | ----- | --------------------------------- | ------------ | -| CentOS | OpenLogic | 7.3 | OpenLogic:CentOS:7.3:7.3.20161221 | 7.3.20161221 | - -I'm comfortable using this image as our base. - -# Summary diff --git a/azure/research.org b/azure/research.org deleted file mode 100644 index e16e02e..0000000 --- a/azure/research.org +++ /dev/null @@ -1,80 +0,0 @@ -# -*- org-use-property-inheritance: t; -*- -#+TITLE: Azure Support -#+AUTHOR: Hippie Hacker -#+EMAIL: hh@ii.coop -#+CREATOR: ii.coop -#+DATE: March 1st, 2017 -#+NOTPROPERTY: header-args :dir ".." -#+NOTPROPERTY: header-args:shell :prologue ". .env_prod ; . ~/.rvm/scripts/rvm" -#+PROPERTY: header-args:shell :session none :exports both :cache yes - -The [[https://www.terraform.io/docs/providers/azure/][Terraform Azure Provider]] needs a publish_settings file - -* Azure Equivalence for Existing EC2 - -** Locating the Equivalent CentOS Image - -Currently the cncf/demo [[https://github.com/cncf/demo/commit/62b4ee750cea96ac18d9998cebed36660b3d5864#diff-165521d9e758a5a743ea42c6fd528156R10][uses the latest CentOS7 AMI]], so let's go find that at Azure. - -#+NAME:list azure default images -#+BEGIN_SRC shell -az vm image list -o table -#+END_SRC - -#+RESULTS[f42a28cef38b0b7c3346005ad6b0128d06d069b1]: list azure default images -| Offer | Publisher | Sku | Urn | UrnAlias | Version | -| ------------- | ---------------------- | ------------------ | -------------------------------------------------------------- | ------------------- | --------- | -| WindowsServer | MicrosoftWindowsServer | 2016-Datacenter | MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest | Win2016Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-R2-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest | Win2012R2Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2008-R2-SP1 | MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest | Win2008R2SP1 | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest | Win2012Datacenter | latest | -| UbuntuServer | Canonical | 14.04.4-LTS | Canonical:UbuntuServer:14.04.4-LTS:latest | UbuntuLTS | latest | -| CentOS | OpenLogic | 7.2 | OpenLogic:CentOS:7.2:latest | CentOS | latest | -| openSUSE | SUSE | 13.2 | SUSE:openSUSE:13.2:latest | openSUSE | latest | -| RHEL | RedHat | 7.2 | RedHat:RHEL:7.2:latest | RHEL | latest | -| SLES | SUSE | 12-SP1 | SUSE:SLES:12-SP1:latest | SLES | latest | -| Debian | credativ | 8 | credativ:Debian:8:latest | Debian | latest | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:latest | CoreOS | latest | - -We are looking for CentOS, and looks like the default Publisher is OpenLogic. Let's see what SKU's they have in westus. - -#+NAME: list OpenLogic CentOS sku offers -#+BEGIN_SRC shell -az vm image list-skus -o table --publisher OpenLogic --location westus --offer CentOS -#+END_SRC - -#+RESULTS[bd6ed1e6f2fc3c614c736373beafd146068045f4]: list OpenLogic CentOS sku offers -| Location | Name | -| ---------- | ------ | -| westus | 6.5 | -| westus | 6.6 | -| westus | 6.7 | -| westus | 6.8 | -| westus | 7.0 | -| westus | 7.1 | -| westus | 7.2 | -| westus | 7.2n | -| westus | 7.3 | - -I prefer not to use 'latest' anywhere, let's grab the precise version of CentOS 7.3. - -#+NAME: list OpenLogic CentOS 7.3 Versions -#+BEGIN_SRC shell -az vm image list --offer centos -o table --publisher openlogic --sku 7.3 --all -#+END_SRC - -#+RESULTS[7a0228f7fd750965697c45a561145ee0cb58e207]: list OpenLogic CentOS 7.3 Versions -| Offer | Publisher | Sku | Urn | Version | -| ------- | ----------- | ----- | --------------------------------- | ------------ | -| CentOS | OpenLogic | 7.3 | OpenLogic:CentOS:7.3:7.3.20161221 | 7.3.20161221 | - -I'm comfortable using this image as our base. - -* Summary - -# Local Variables: -# eval: (require (quote ob-shell)) -# eval: (require (quote ob-lisp)) -# eval: (org-babel-do-load-languages 'org-babel-load-languages '((js . t) (shell . t))) -# eval: (setenv "PATH" (concat (concat (getenv "HOME") "/bin:") (getenv "PATH") )) -# End: From 708d15444983ffc6576d071e4d6e2f8c3b821839 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 16 Mar 2017 06:00:09 +1300 Subject: [PATCH 002/149] Refactor AWS --- Dockerfile | 5 +---- init-cfssl => aws/init-cfssl | 0 io.tf => aws/io.tf | 0 modules.tf => aws/modules.tf | 0 {modules => aws/modules}/bastion/ec2.tf | 0 {modules => aws/modules}/bastion/iam.tf | 0 {modules => aws/modules}/bastion/io.tf | 0 {modules => aws/modules}/bastion/user-data.yml | 0 {modules => aws/modules}/etcd/cloud-config.tf | 0 {modules => aws/modules}/etcd/cloud-config.yml | 0 {modules => aws/modules}/etcd/ec2.tf | 0 {modules => aws/modules}/etcd/elb.tf | 0 {modules => aws/modules}/etcd/io.tf | 0 {modules => aws/modules}/iam/etcd.tf | 0 {modules => aws/modules}/iam/io.tf | 0 {modules => aws/modules}/iam/worker.tf | 0 {modules => aws/modules}/kubeconfig/io.tf | 0 .../modules}/kubeconfig/kubeconfig.tf | 0 {modules => aws/modules}/route53/io.tf | 0 {modules => aws/modules}/route53/route53.tf | 0 {modules => aws/modules}/s3/io.tf | 0 {modules => aws/modules}/s3/s3-cp | 0 {modules => aws/modules}/s3/s3.tf | 0 {modules => aws/modules}/security/io.tf | 0 {modules => aws/modules}/security/security.tf | 0 {modules => aws/modules}/vpc-existing/io.tf | 0 {modules => aws/modules}/vpc/io.tf | 0 {modules => aws/modules}/vpc/private.tf | 0 {modules => aws/modules}/vpc/public.tf | 0 {modules => aws/modules}/vpc/vpc.tf | 0 {modules => aws/modules}/worker/cloud-config.tf | 0 {modules => aws/modules}/worker/cloud-config.yml | 0 {modules => aws/modules}/worker/ec2.tf | 0 {modules => aws/modules}/worker/io.tf | 0 modules_override.tf => aws/modules_override.tf | 0 entrypoint.sh | 16 ++++------------ 36 files changed, 5 insertions(+), 16 deletions(-) rename init-cfssl => aws/init-cfssl (100%) rename io.tf => aws/io.tf (100%) rename modules.tf => aws/modules.tf (100%) rename {modules => aws/modules}/bastion/ec2.tf (100%) rename {modules => aws/modules}/bastion/iam.tf (100%) rename {modules => aws/modules}/bastion/io.tf (100%) rename {modules => aws/modules}/bastion/user-data.yml (100%) rename {modules => aws/modules}/etcd/cloud-config.tf (100%) rename {modules => aws/modules}/etcd/cloud-config.yml (100%) rename {modules => aws/modules}/etcd/ec2.tf (100%) rename {modules => aws/modules}/etcd/elb.tf (100%) rename {modules => aws/modules}/etcd/io.tf (100%) rename {modules => aws/modules}/iam/etcd.tf (100%) rename {modules => aws/modules}/iam/io.tf (100%) rename {modules => aws/modules}/iam/worker.tf (100%) rename {modules => aws/modules}/kubeconfig/io.tf (100%) rename {modules => aws/modules}/kubeconfig/kubeconfig.tf (100%) rename {modules => aws/modules}/route53/io.tf (100%) rename {modules => aws/modules}/route53/route53.tf (100%) rename {modules => aws/modules}/s3/io.tf (100%) rename {modules => aws/modules}/s3/s3-cp (100%) rename {modules => aws/modules}/s3/s3.tf (100%) rename {modules => aws/modules}/security/io.tf (100%) rename {modules => aws/modules}/security/security.tf (100%) rename {modules => aws/modules}/vpc-existing/io.tf (100%) rename {modules => aws/modules}/vpc/io.tf (100%) rename {modules => aws/modules}/vpc/private.tf (100%) rename {modules => aws/modules}/vpc/public.tf (100%) rename {modules => aws/modules}/vpc/vpc.tf (100%) rename {modules => aws/modules}/worker/cloud-config.tf (100%) rename {modules => aws/modules}/worker/cloud-config.yml (100%) rename {modules => aws/modules}/worker/ec2.tf (100%) rename {modules => aws/modules}/worker/io.tf (100%) rename modules_override.tf => aws/modules_override.tf (100%) diff --git a/Dockerfile b/Dockerfile index 911cead..3efefb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,11 +27,8 @@ RUN go get -u github.com/cloudflare/cfssl/cmd/cfssl && \ go get -u github.com/cloudflare/cfssl/cmd/... WORKDIR /cncf -COPY AddOns /cncf/AddOns -COPY Demo /cncf/Demo -COPY modules /cncf/modules -COPY io.tf modules.tf modules_override.tf vpc-existing.tfvars terraform.tfvars wait-for-cluster init-cfssl /cncf/ COPY entrypoint.sh /cncf/ +COPY aws /aws/ RUN chmod +x /cncf/entrypoint.sh diff --git a/init-cfssl b/aws/init-cfssl similarity index 100% rename from init-cfssl rename to aws/init-cfssl diff --git a/io.tf b/aws/io.tf similarity index 100% rename from io.tf rename to aws/io.tf diff --git a/modules.tf b/aws/modules.tf similarity index 100% rename from modules.tf rename to aws/modules.tf diff --git a/modules/bastion/ec2.tf b/aws/modules/bastion/ec2.tf similarity index 100% rename from modules/bastion/ec2.tf rename to aws/modules/bastion/ec2.tf diff --git a/modules/bastion/iam.tf b/aws/modules/bastion/iam.tf similarity index 100% rename from modules/bastion/iam.tf rename to aws/modules/bastion/iam.tf diff --git a/modules/bastion/io.tf b/aws/modules/bastion/io.tf similarity index 100% rename from modules/bastion/io.tf rename to aws/modules/bastion/io.tf diff --git a/modules/bastion/user-data.yml b/aws/modules/bastion/user-data.yml similarity index 100% rename from modules/bastion/user-data.yml rename to aws/modules/bastion/user-data.yml diff --git a/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf similarity index 100% rename from modules/etcd/cloud-config.tf rename to aws/modules/etcd/cloud-config.tf diff --git a/modules/etcd/cloud-config.yml b/aws/modules/etcd/cloud-config.yml similarity index 100% rename from modules/etcd/cloud-config.yml rename to aws/modules/etcd/cloud-config.yml diff --git a/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf similarity index 100% rename from modules/etcd/ec2.tf rename to aws/modules/etcd/ec2.tf diff --git a/modules/etcd/elb.tf b/aws/modules/etcd/elb.tf similarity index 100% rename from modules/etcd/elb.tf rename to aws/modules/etcd/elb.tf diff --git a/modules/etcd/io.tf b/aws/modules/etcd/io.tf similarity index 100% rename from modules/etcd/io.tf rename to aws/modules/etcd/io.tf diff --git a/modules/iam/etcd.tf b/aws/modules/iam/etcd.tf similarity index 100% rename from modules/iam/etcd.tf rename to aws/modules/iam/etcd.tf diff --git a/modules/iam/io.tf b/aws/modules/iam/io.tf similarity index 100% rename from modules/iam/io.tf rename to aws/modules/iam/io.tf diff --git a/modules/iam/worker.tf b/aws/modules/iam/worker.tf similarity index 100% rename from modules/iam/worker.tf rename to aws/modules/iam/worker.tf diff --git a/modules/kubeconfig/io.tf b/aws/modules/kubeconfig/io.tf similarity index 100% rename from modules/kubeconfig/io.tf rename to aws/modules/kubeconfig/io.tf diff --git a/modules/kubeconfig/kubeconfig.tf b/aws/modules/kubeconfig/kubeconfig.tf similarity index 100% rename from modules/kubeconfig/kubeconfig.tf rename to aws/modules/kubeconfig/kubeconfig.tf diff --git a/modules/route53/io.tf b/aws/modules/route53/io.tf similarity index 100% rename from modules/route53/io.tf rename to aws/modules/route53/io.tf diff --git a/modules/route53/route53.tf b/aws/modules/route53/route53.tf similarity index 100% rename from modules/route53/route53.tf rename to aws/modules/route53/route53.tf diff --git a/modules/s3/io.tf b/aws/modules/s3/io.tf similarity index 100% rename from modules/s3/io.tf rename to aws/modules/s3/io.tf diff --git a/modules/s3/s3-cp b/aws/modules/s3/s3-cp similarity index 100% rename from modules/s3/s3-cp rename to aws/modules/s3/s3-cp diff --git a/modules/s3/s3.tf b/aws/modules/s3/s3.tf similarity index 100% rename from modules/s3/s3.tf rename to aws/modules/s3/s3.tf diff --git a/modules/security/io.tf b/aws/modules/security/io.tf similarity index 100% rename from modules/security/io.tf rename to aws/modules/security/io.tf diff --git a/modules/security/security.tf b/aws/modules/security/security.tf similarity index 100% rename from modules/security/security.tf rename to aws/modules/security/security.tf diff --git a/modules/vpc-existing/io.tf b/aws/modules/vpc-existing/io.tf similarity index 100% rename from modules/vpc-existing/io.tf rename to aws/modules/vpc-existing/io.tf diff --git a/modules/vpc/io.tf b/aws/modules/vpc/io.tf similarity index 100% rename from modules/vpc/io.tf rename to aws/modules/vpc/io.tf diff --git a/modules/vpc/private.tf b/aws/modules/vpc/private.tf similarity index 100% rename from modules/vpc/private.tf rename to aws/modules/vpc/private.tf diff --git a/modules/vpc/public.tf b/aws/modules/vpc/public.tf similarity index 100% rename from modules/vpc/public.tf rename to aws/modules/vpc/public.tf diff --git a/modules/vpc/vpc.tf b/aws/modules/vpc/vpc.tf similarity index 100% rename from modules/vpc/vpc.tf rename to aws/modules/vpc/vpc.tf diff --git a/modules/worker/cloud-config.tf b/aws/modules/worker/cloud-config.tf similarity index 100% rename from modules/worker/cloud-config.tf rename to aws/modules/worker/cloud-config.tf diff --git a/modules/worker/cloud-config.yml b/aws/modules/worker/cloud-config.yml similarity index 100% rename from modules/worker/cloud-config.yml rename to aws/modules/worker/cloud-config.yml diff --git a/modules/worker/ec2.tf b/aws/modules/worker/ec2.tf similarity index 100% rename from modules/worker/ec2.tf rename to aws/modules/worker/ec2.tf diff --git a/modules/worker/io.tf b/aws/modules/worker/io.tf similarity index 100% rename from modules/worker/io.tf rename to aws/modules/worker/io.tf diff --git a/modules_override.tf b/aws/modules_override.tf similarity index 100% rename from modules_override.tf rename to aws/modules_override.tf diff --git a/entrypoint.sh b/entrypoint.sh index 4e6c2bc..8024113 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -22,16 +22,8 @@ fi # Run CMD if [ "$@" = "" ] ; then echo $@ not handled yet -elif [ "$1" = "deploy-cloud" ] ; then - make all || true # more logic later - kubectl proxy -elif [ "$1" = "destroy" ] ; then - #make .addons - #kubectl delete -f .addons/ --recursive || true - # kubernetes creates a load balancer on it's own - # we need to destroy it before terraform destroy - make terraform.tfvars - terraform get - make clean - #make destroy +elif [ "$1" = "deploy-aws" ] ; then + cd /aws && terraform get && terrafrom apply +elif [ "$1" = "destroy-aws" ] ; then + cd /aws && terraform destroy -force fi From bff551d637ef6be595c5de34c7b164bb8a11ed3b Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 16 Mar 2017 06:25:03 +1300 Subject: [PATCH 003/149] TF Vars --- Dockerfile | 1 - aws/init-tfvars.sh | 75 ++++++++++++++++++++++++ wait-for-cluster => aws/wait-for-cluster | 0 vpc-existing.tfvars | 15 ----- 4 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 aws/init-tfvars.sh rename wait-for-cluster => aws/wait-for-cluster (100%) delete mode 100644 vpc-existing.tfvars diff --git a/Dockerfile b/Dockerfile index 3efefb4..f102ea0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,5 @@ COPY entrypoint.sh /cncf/ COPY aws /aws/ RUN chmod +x /cncf/entrypoint.sh - #ENTRYPOINT ["/cncf/entrypoint.sh"] CMD ["/bin/bash"] \ No newline at end of file diff --git a/aws/init-tfvars.sh b/aws/init-tfvars.sh new file mode 100644 index 0000000..ec31506 --- /dev/null +++ b/aws/init-tfvars.sh @@ -0,0 +1,75 @@ +#!/bin/bash -e + +function usage { + echo "USAGE: $0 " + echo " example: $0 us-west-1 stable hvm k8s-testing testing.k8s testing 0.0.0.0/0 10.0.0.0/16 10.2.0.0/16 10.3.0.0/24 10.3.0.1 10.3.0.10 10.0.10.10,10.0.10.11,10.0.10.12 quay.io/coreos/hyperkube v1.4.6_coreos.0" + + exit 1 +} + +if [[ $# -ne 15 ]]; then + usage +fi + +AWS_REGION="$1" +COREOS_CHANNEL="$2" +COREOS_VM_TYPE="$3" +AWS_EC2_KEY_NAME="$4" +INTERNAL_TLD="$5" +CLUSTER_NAME="$6" +CIDR_ALLOW_SSH="$7" +CIDR_VPC="$8" +CIDR_PODS="$9" +CIDR_SERVICE_CLUSTER="${10}" +K8S_SERVICE_IP="${11}" +K8S_DNS_IP="${12}" +ETCD_IPS="${13}" +HYPERKUBE_IMAGE="${14}" +HYPERKUBE_TAG="${15}" + +COREOS_AMI_ID=`curl -s \ + $(printf "http://%s.release.core-os.net/amd64-usr/current/coreos_production_ami_%s_%s.txt" \ + $COREOS_CHANNEL $COREOS_VM_TYPE $AWS_REGION)` + +AWS_ACCOUNT_ID=`aws iam get-user --output json \ + | awk '/arn:aws:/{print $2}' \ + | grep -Eo '[[:digit:]]{12}'` + +AWS_REGION_AZS=`aws ec2 describe-availability-zones --region ${AWS_REGION} --output json \ + | jq --raw-output '.AvailabilityZones | map(.ZoneName) | .[]' \ + | xargs \ + | sed -e 's/ /,/g'` + + +cat < terraform.tfvars +# Generated by scripts/init-variables.sh +aws = { + account-id = "${AWS_ACCOUNT_ID}" + azs = "${AWS_REGION_AZS}" + key-name = "${AWS_EC2_KEY_NAME}" + region = "${AWS_REGION}" +} +cidr = { + allow-ssh = "${CIDR_ALLOW_SSH}" + pods = "${CIDR_PODS}" + service-cluster = "${CIDR_SERVICE_CLUSTER}" + vpc = "${CIDR_VPC}" +} +coreos-aws = { + ami = "${COREOS_AMI_ID}" + channel = "${COREOS_CHANNEL}" + type = "${COREOS_VM_TYPE}" +} +k8s = { + hyperkube-image = "${HYPERKUBE_IMAGE}" + hyperkube-tag = "${HYPERKUBE_TAG}" +} +dns-service-ip = "${K8S_DNS_IP}" +internal-tld = "${INTERNAL_TLD}" +k8s-service-ip = "${K8S_SERVICE_IP}" +name = "${CLUSTER_NAME}" +s3-bucket = "${AWS_ACCOUNT_ID}-${CLUSTER_NAME}-${AWS_REGION}" +etcd-ips = "$ETCD_IPS" +EOF +if [ -f ./vpc-existing.tfvars ]; then cat ./vpc-existing.tfvars >>terraform.tfvars ; fi +cat terraform.tfvars diff --git a/wait-for-cluster b/aws/wait-for-cluster similarity index 100% rename from wait-for-cluster rename to aws/wait-for-cluster diff --git a/vpc-existing.tfvars b/vpc-existing.tfvars deleted file mode 100644 index ae540d2..0000000 --- a/vpc-existing.tfvars +++ /dev/null @@ -1,15 +0,0 @@ -# # This is merged in with terraform.tfvars for override/existing VPC purposes. Only to be used in conjunction with modules_override.tf - -# # The existing VPC CIDR range, ensure that the the etcd, controller and worker IPs are in this range -# cidr.vpc = "10.0.0.0/16" - -# # etcd server static IPs, ensure that they fall within the exisiting VPC public subnet range -# etcd-ips = "10.0.0.10,10.0.0.11,10.0.0.12" - -# # Put your existing VPC info here: -# vpc-existing { -# id = "vpc-" -# gateway-id = "igw-" -# subnet-ids-public = "subnet-,subnet-" -# subnet-ids-private = "subnet-,subnet-" -# } \ No newline at end of file From 0887fa37da2705b4767a3e7bf8891b37711675a0 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 16 Mar 2017 09:49:34 +1300 Subject: [PATCH 004/149] Refactor AWS Code for Docker Deploy --- Dockerfile | 7 ++++--- aws/modules.tf | 1 + aws/modules/s3/io.tf | 1 + aws/modules/s3/s3-cp | 4 +++- aws/modules/s3/s3.tf | 2 +- entrypoint.sh | 6 +++--- terraform.tfvars | 44 -------------------------------------------- 7 files changed, 13 insertions(+), 52 deletions(-) delete mode 100644 terraform.tfvars diff --git a/Dockerfile b/Dockerfile index f102ea0..a0b78d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,10 +26,11 @@ RUN go get -u github.com/cloudflare/cfssl/cmd/cfssl && \ #Add Terraform Modules go get -u github.com/cloudflare/cfssl/cmd/... -WORKDIR /cncf +WORKDIR /cncf/data COPY entrypoint.sh /cncf/ COPY aws /aws/ RUN chmod +x /cncf/entrypoint.sh -#ENTRYPOINT ["/cncf/entrypoint.sh"] -CMD ["/bin/bash"] \ No newline at end of file +ENTRYPOINT ["/cncf/entrypoint.sh"] +CMD ["deploy-aws"] +#CMD ["/bin/bash"] \ No newline at end of file diff --git a/aws/modules.tf b/aws/modules.tf index e839ef7..50a780a 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -9,6 +9,7 @@ module "s3" { name = "${ var.name }" region = "${ var.aws["region"] }" service-cluster-ip-range = "${ var.cidr["service-cluster"] }" + dir-ssl = "${ var.dir-ssl }" } module "vpc" { diff --git a/aws/modules/s3/io.tf b/aws/modules/s3/io.tf index 9948911..b107d1c 100644 --- a/aws/modules/s3/io.tf +++ b/aws/modules/s3/io.tf @@ -6,6 +6,7 @@ variable "internal-tld" {} variable "name" {} variable "region" {} variable "service-cluster-ip-range" {} +variable "dir-ssl" {} output "bucket-prefix" { value = "${ var.bucket-prefix }" } output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } diff --git a/aws/modules/s3/s3-cp b/aws/modules/s3/s3-cp index 98b961f..75b48e8 100755 --- a/aws/modules/s3/s3-cp +++ b/aws/modules/s3/s3-cp @@ -33,13 +33,16 @@ BUCKET=$1 [ -z "$HYPERKUBE" ] && usage [ -z "$INTERNAL_TLD" ] && usage [ -z "$REGION" ] && usage +[ -z "$DIR_SSL"] && usage # print environment and parameter echo "❤ s3-cp using these settings" echo envvar HYPERKUBE=${HYPERKUBE} echo envvar INTERNAL_TLD=${INTERNAL_TLD} echo envvar REGION=${REGION} +echo envvar DIR_SSL=${DIR_SSL} echo parameter BUCKET=${BUCKET} + # FIXME: move this into tfvars #echo envvar DIR_SSL=data/.cfssl echo "✓ Print environment variables and parameters success" @@ -47,7 +50,6 @@ echo "✓ Print environment variables and parameters success" set -o nounset set -o pipefail -DIR_SSL=data/.cfssl # verify directories exist [ -d "$DIR_SSL" ] || (echo "✗ $DIR_SSL doesn't exist" && exit 1) diff --git a/aws/modules/s3/s3.tf b/aws/modules/s3/s3.tf index fd047d3..1f37f21 100644 --- a/aws/modules/s3/s3.tf +++ b/aws/modules/s3/s3.tf @@ -12,11 +12,11 @@ resource "aws_s3_bucket" "ssl" { provisioner "local-exec" { command = <data/awsconfig + cat </cncf/data/awsconfig [default] output = ${AWS_DEFAULT_OUTPUT:-json} region = ${AWS_DEFAULT_REGION:-ap-southeast-2} @@ -23,7 +23,7 @@ fi if [ "$@" = "" ] ; then echo $@ not handled yet elif [ "$1" = "deploy-aws" ] ; then - cd /aws && terraform get && terrafrom apply + terraform get /aws && terraform apply /aws elif [ "$1" = "destroy-aws" ] ; then - cd /aws && terraform destroy -force + terraform destroy /aws -force fi diff --git a/terraform.tfvars b/terraform.tfvars deleted file mode 100644 index e49396e..0000000 --- a/terraform.tfvars +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by scripts/init-variables.sh -aws = { - account-id = "751789298977" - azs = "ap-southeast-2a,ap-southeast-2b,ap-southeast-2c" - key-name = "ii-cncfdemo" - region = "ap-southeast-2" -} -cidr = { - allow-ssh = "103.26.16.226/32" - pods = "10.2.0.0/16" - service-cluster = "10.3.0.0/24" - vpc = "10.0.0.0/16" -} -coreos-aws = { - ami = "ami-fde3e09e" - channel = "stable" - type = "hvm" -} -k8s = { - hyperkube-image = "quay.io/coreos/hyperkube" - hyperkube-tag = "v1.4.7_coreos.0" -} -dns-service-ip = "10.3.0.10" -internal-tld = "test.kz8s" -k8s-service-ip = "10.3.0.1" -name = "test" -s3-bucket = "751789298977-test-ap-southeast-2" -etcd-ips = "10.0.10.10,10.0.10.11,10.0.10.12" -dir-ssl = "/cncf/data/.cfssl" -# # This is merged in with terraform.tfvars for override/existing VPC purposes. Only to be used in conjunction with modules_override.tf - -# # The existing VPC CIDR range, ensure that the the etcd, controller and worker IPs are in this range -# cidr.vpc = "10.0.0.0/16" - -# # etcd server static IPs, ensure that they fall within the exisiting VPC public subnet range -# etcd-ips = "10.0.0.10,10.0.0.11,10.0.0.12" - -# # Put your existing VPC info here: -# vpc-existing { -# id = "vpc-" -# gateway-id = "igw-" -# subnet-ids-public = "subnet-,subnet-" -# subnet-ids-private = "subnet-,subnet-" -# } From e56a8c7445d73a0e1d03371533fd736b9f103650 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 16 Mar 2017 12:39:17 +1300 Subject: [PATCH 005/149] Update Dockerfile/entrypoint + Add Readme --- Dockerfile | 2 +- aws/Readme.mkd | 77 +++++++++++++++++++++++++++++++++++++++++++ data/terraform.tfvars | 43 ++++++++++++++++++++++++ entrypoint.sh | 8 ++--- 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 aws/Readme.mkd create mode 100644 data/terraform.tfvars diff --git a/Dockerfile b/Dockerfile index a0b78d6..ae72fb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,5 +32,5 @@ COPY aws /aws/ RUN chmod +x /cncf/entrypoint.sh ENTRYPOINT ["/cncf/entrypoint.sh"] -CMD ["deploy-aws"] +CMD ["aws"] #CMD ["/bin/bash"] \ No newline at end of file diff --git a/aws/Readme.mkd b/aws/Readme.mkd new file mode 100644 index 0000000..d528183 --- /dev/null +++ b/aws/Readme.mkd @@ -0,0 +1,77 @@ +## Prerequisites +* [docker](https://docker.io/) + +* AWS User with following Permissions: + - AmazonEC2FullAccess + - AmazonS3FullAccess + - AWSCodeDeployFullAccess + - AmazonRoute53DomainsFullAccess + - AmazonRoute53FullAccess + - IAMFullAccess + - IAMUserChangePassword + +* update data/terraform.tfvars + +Contains config. + +## export AWS Authentication + +``` +export AWS_ACCESS_KEY_ID="YOUR_AWS_KEY_ID" +export AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_KEY" +``` + +## create AWS Kubernetes Endpoint + +``` +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data awstest deploy-aws +``` + +## configure kubectl on local system + +``` +sudo chown -R $(whoami):$(whoami) data/ +export KUBECONFIG=$(pwd)/data/kubeconfig +$ kubectl get nodes +NAME STATUS AGE +ip-10-0-10-10.ap-southeast-2.compute.internal Ready,SchedulingDisabled 7m +ip-10-0-10-11.ap-southeast-2.compute.internal Ready,SchedulingDisabled 6m +ip-10-0-10-12.ap-southeast-2.compute.internal Ready,SchedulingDisabled 7m +ip-10-0-10-51.ap-southeast-2.compute.internal Ready 6m +ip-10-0-11-7.ap-southeast-2.compute.internal Ready 6m +ip-10-0-12-68.ap-southeast-2.compute.internal Ready 6m +``` + +## data folder contains certs + kubeconfig + +It also contains a json file containing details on current cluster state. + +``` +$ sudo cat ./data/kubeconfig +apiVersion: v1 +clusters: +- cluster: + certificate-authority: .cfssl/ca.pem + server: https://kz8s-apiserver-test-453655923.ap-southeast-2.elb.amazonaws.com + name: cluster-test +contexts: +- context: + cluster: cluster-test + user: admin-test + name: test +current-context: test +kind: Config +preferences: {} +users: +- name: admin-test + user: + client-certificate: .cfssl/k8s-admin.pem + client-key: .cfssl/k8s-admin-key.pem +``` + + +## destroy AWS Kubernetes Endpoint + +``` +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data awstest destroy-aws +``` diff --git a/data/terraform.tfvars b/data/terraform.tfvars new file mode 100644 index 0000000..b5d01ae --- /dev/null +++ b/data/terraform.tfvars @@ -0,0 +1,43 @@ +aws = { + account-id = "751789298977" + azs = "ap-southeast-2a,ap-southeast-2b,ap-southeast-2c" + key-name = "ii-cncfdemo" + region = "ap-southeast-2" +} +cidr = { + allow-ssh = "103.26.16.226/32" + pods = "10.2.0.0/16" + service-cluster = "10.3.0.0/24" + vpc = "10.0.0.0/16" +} +coreos-aws = { + ami = "ami-fde3e09e" + channel = "stable" + type = "hvm" +} +k8s = { + hyperkube-image = "quay.io/coreos/hyperkube" + hyperkube-tag = "v1.4.7_coreos.0" +} +dns-service-ip = "10.3.0.10" +internal-tld = "test.kz8s" +k8s-service-ip = "10.3.0.1" +name = "test" +s3-bucket = "751789298977-test-ap-southeast-2" +etcd-ips = "10.0.10.10,10.0.10.11,10.0.10.12" +dir-ssl = "/cncf/data/.cfssl" +# # This is merged in with terraform.tfvars for override/existing VPC purposes. Only to be used in conjunction with modules_override.tf + +# # The existing VPC CIDR range, ensure that the the etcd, controller and worker IPs are in this range +# cidr.vpc = "10.0.0.0/16" + +# # etcd server static IPs, ensure that they fall within the exisiting VPC public subnet range +# etcd-ips = "10.0.0.10,10.0.0.11,10.0.0.12" + +# # Put your existing VPC info here: +# vpc-existing { +# id = "vpc-" +# gateway-id = "igw-" +# subnet-ids-public = "subnet-,subnet-" +# subnet-ids-private = "subnet-,subnet-" +# } diff --git a/entrypoint.sh b/entrypoint.sh index c808439..643b5df 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -22,8 +22,8 @@ fi # Run CMD if [ "$@" = "" ] ; then echo $@ not handled yet -elif [ "$1" = "deploy-aws" ] ; then - terraform get /aws && terraform apply /aws -elif [ "$1" = "destroy-aws" ] ; then - terraform destroy /aws -force +elif [ "$1" = "aws" ] ; then + terraform get /aws && time terraform apply /aws +elif [ "$1" = "aws-destroy" ] ; then + time terraform destroy -force /aws fi From db9f573c6e03609c72c244be0f78fa40d5a3b94b Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 16 Mar 2017 12:40:23 +1300 Subject: [PATCH 006/149] Remove # --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ae72fb5..9e2ef8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,5 +32,4 @@ COPY aws /aws/ RUN chmod +x /cncf/entrypoint.sh ENTRYPOINT ["/cncf/entrypoint.sh"] -CMD ["aws"] -#CMD ["/bin/bash"] \ No newline at end of file +CMD ["aws"] \ No newline at end of file From c9023c21535655b89508bbe946c7767591f4da9b Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 16 Mar 2017 16:27:03 +1300 Subject: [PATCH 007/149] Add Destroy + Update Readme --- aws/Readme.mkd | 4 ++-- aws/io.tf | 34 +++++++++++++++++++++++----------- entrypoint.sh | 5 ++++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/aws/Readme.mkd b/aws/Readme.mkd index d528183..793cd1e 100644 --- a/aws/Readme.mkd +++ b/aws/Readme.mkd @@ -24,7 +24,7 @@ export AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_KEY" ## create AWS Kubernetes Endpoint ``` -docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data awstest deploy-aws +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data create/aws ``` ## configure kubectl on local system @@ -73,5 +73,5 @@ users: ## destroy AWS Kubernetes Endpoint ``` -docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data awstest destroy-aws +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data terminate/aws ``` diff --git a/aws/io.tf b/aws/io.tf index 7a7f6fe..4f663e9 100644 --- a/aws/io.tf +++ b/aws/io.tf @@ -81,14 +81,6 @@ ${ var.dir-ssl } \ ${ var.aws["region" ] } \ ${ var.internal-tld } \ ${ var.k8s-service-ip } -EOF - } - - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = < ${ var.dir-key-pair }/${ var.aws["key-name"] }.pem chmod 400 ${ var.dir-key-pair }/${ var.aws["key-name"] }.pem EOF - } + } + +} + +resource "null_resource" "dummy_dependency2" { + depends_on = [ "null_resource.aws_keypair" ] +} + + +# Clean-up Destroy +resource "null_resource" "cleanup" { provisioner "local-exec" { when = "destroy" @@ -118,7 +120,17 @@ EOF command = < Date: Fri, 17 Mar 2017 04:37:06 +1300 Subject: [PATCH 008/149] Reformat for readability --- aws/Readme.mkd | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/aws/Readme.mkd b/aws/Readme.mkd index 793cd1e..dd419d2 100644 --- a/aws/Readme.mkd +++ b/aws/Readme.mkd @@ -10,9 +10,7 @@ - IAMFullAccess - IAMUserChangePassword -* update data/terraform.tfvars - -Contains config. +* optionally update config data/terraform.tfvars ## export AWS Authentication @@ -24,7 +22,9 @@ export AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_KEY" ## create AWS Kubernetes Endpoint ``` -docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data create/aws +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -v $(pwd)/data:/cncf/data create/aws ``` ## configure kubectl on local system @@ -73,5 +73,7 @@ users: ## destroy AWS Kubernetes Endpoint ``` -docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -v $(pwd)/data:/cncf/data terminate/aws +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -v $(pwd)/data:/cncf/data terminate/aws ``` From 4d4c0783fd9f396c46854ef83e4941c7f23aec50 Mon Sep 17 00:00:00 2001 From: dlx Date: Tue, 21 Mar 2017 09:07:59 +1300 Subject: [PATCH 009/149] Fix Readme --- aws/Readme.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/Readme.mkd b/aws/Readme.mkd index dd419d2..feefc27 100644 --- a/aws/Readme.mkd +++ b/aws/Readme.mkd @@ -73,7 +73,7 @@ users: ## destroy AWS Kubernetes Endpoint ``` -docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY \ +docker run -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -v $(pwd)/data:/cncf/data terminate/aws ``` From 7d565c4c9ed9020dce73f3f8ff7c85da9a8199b4 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 10 Apr 2017 11:09:04 +1200 Subject: [PATCH 010/149] TF_VARS needs underscores and not dashes --- azure/cert.tf | 2 +- azure/input.tf | 40 ++++++++--------- azure/modules.tf | 62 +++++++++++++-------------- azure/modules/bastion/input.tf | 12 +++--- azure/modules/bastion/node.tf | 14 +++--- azure/modules/etcd/.#cloud-config.tf | 1 - azure/modules/etcd/cloud-config.tf | 14 +++--- azure/modules/etcd/cloud-config.yml | 18 ++++---- azure/modules/etcd/input.tf | 26 +++++------ azure/modules/etcd/nodes.tf | 18 ++++---- azure/modules/worker/cloud-config.tf | 8 ++-- azure/modules/worker/cloud-config.yml | 10 ++--- azure/modules/worker/input.tf | 22 +++++----- azure/modules/worker/nodes.tf | 22 +++++----- azure/output.tf | 6 +-- azure/readme.org | 22 +++++----- 16 files changed, 148 insertions(+), 149 deletions(-) delete mode 120000 azure/modules/etcd/.#cloud-config.tf diff --git a/azure/cert.tf b/azure/cert.tf index 49eeb10..0e7bc50 100644 --- a/azure/cert.tf +++ b/azure/cert.tf @@ -7,7 +7,7 @@ ${ path.module }/init-cfssl \ ${ var.data-dir }/.cfssl \ ${ azurerm_resource_group.cncf.location } \ ${ var.internal-tld } \ -${ var.k8s-service-ip } +${ var.k8s_service_ip } EOF } diff --git a/azure/input.tf b/azure/input.tf index b47ad99..272e574 100644 --- a/azure/input.tf +++ b/azure/input.tf @@ -1,24 +1,24 @@ variable "name" { default = "azure" } -variable "internal-tld" { default = "cncf.demo" } -variable "master-node-count" { default = "3" } -variable "worker-node-count" { default = "3" } -variable "master-vm-size" { default = "Standard_A2" } -variable "worker-vm-size" { default = "Standard_A2" } -variable "bastion-vm-size" { default = "Standard_A2" } +variable "internal_tld" { default = "azure.cncf.demo" } +variable "master_node_count" { default = "3" } +variable "worker_node_count" { default = "3" } +variable "master_vm_size" { default = "Standard_A2" } +variable "worker_vm_size" { default = "Standard_A2" } +variable "bastion_vm_size" { default = "Standard_A2" } # Set from https://quay.io/repository/coreos/hyperkube?tab=tags -variable "kubelet-image-url" { default = "quay.io/coreos/hyperkube"} -variable "kubelet-image-tag" { default = "v1.4.7_coreos.0"} -variable "image-publisher" { default = "CoreOS" } -variable "image-offer" { default = "CoreOS" } -variable "image-sku" { default = "Stable" } -variable "image-version" { default = "1298.6.0" } +variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} +variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} +variable "image_publisher" { default = "CoreOS" } +variable "image_offer" { default = "CoreOS" } +variable "image_sku" { default = "Stable" } +variable "image_version" { default = "1298.6.0" } variable "location" { default = "westus" } -variable "cluster-domain" { default = "cluster.local" } -variable "admin-username" { default = "cncf"} -variable "vpc-cidr" { default = "10.0.0.0/16" } -variable "pod-cidr" { default = "10.2.0.0/16" } -variable "service-cidr" { default = "10.3.0.0/24" } -variable "k8s-service-ip" { default = "10.3.0.1" } -variable "dns-service-ip" { default = "10.3.0.10" } -variable "allow-ssh-cidr" { default = "0.0.0.0/0" } +variable "cluster_domain" { default = "cluster.local" } +variable "admin_username" { default = "cncf"} +variable "vpc_cidr" { default = "10.0.0.0/16" } +variable "pod_cidr" { default = "10.2.0.0/16" } +variable "service_cidr" { default = "10.3.0.0/24" } +variable "k8s_service_ip" { default = "10.3.0.1" } +variable "dns_service_ip" { default = "10.3.0.10" } +variable "allow_ssh_cidr" { default = "0.0.0.0/0" } variable "data-dir" { default = "/cncf/data" } diff --git a/azure/modules.tf b/azure/modules.tf index d854dac..91219ac 100644 --- a/azure/modules.tf +++ b/azure/modules.tf @@ -1,7 +1,7 @@ module "vpc" { source = "./modules/vpc" name = "${ var.name }" - cidr = "${ var.vpc-cidr }" + cidr = "${ var.vpc_cidr }" name-servers-file = "${ module.dns.name-servers-file }" location = "${ var.location }" } @@ -17,25 +17,25 @@ module "dns" { source = "./modules/etcd" name = "${ var.name }" location = "${ var.location }" - admin-username = "${ var.admin-username }" - master-node-count = "${ var.master-node-count }" - master-vm-size = "${ var.master-vm-size }" - image-publisher = "${ var.image-publisher }" - image-offer = "${ var.image-offer }" - image-sku = "${ var.image-sku }" - image-version = "${ var.image-version }" + admin_username = "${ var.admin_username }" + master_node_count = "${ var.master_node_count }" + master_vm_size = "${ var.master_vm_size }" + image_publisher = "${ var.image_publisher }" + image_offer = "${ var.image_offer }" + image_sku = "${ var.image_sku }" + image_version = "${ var.image_version }" subnet-id = "${ module.vpc.subnet-id }" storage-account = "${ azurerm_storage_account.cncf.name }" storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" storage-container = "${ azurerm_storage_container.cncf.name }" availability-id = "${ azurerm_availability_set.cncf.id }" - cluster-domain = "${ var.cluster-domain }" - kubelet-image-url = "${ var.kubelet-image-url }" - kubelet-image-tag = "${ var.kubelet-image-tag }" - dns-service-ip = "${ var.dns-service-ip }" + cluster_domain = "${ var.cluster_domain }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + dns_service_ip = "${ var.dns_service_ip }" internal-tld = "${ var.internal-tld }" - pod-cidr = "${ var.pod-cidr }" - service-cidr = "${ var.service-cidr }" + pod_cidr = "${ var.pod_cidr }" + service_cidr = "${ var.service_cidr }" k8s-apiserver-tar = "${file("${ var.data-dir }/.cfssl/k8s-apiserver.tar")}" cloud-config = "${file("${ var.data-dir }/azure-config.json")}" # etcd-security-group-id = "${ module.security.etcd-id }" @@ -47,12 +47,12 @@ module "bastion" { source = "./modules/bastion" name = "${ var.name }" location = "${ var.location }" - bastion-vm-size = "${ var.bastion-vm-size }" - image-publisher = "${ var.image-publisher }" - image-offer = "${ var.image-offer }" - image-sku = "${ var.image-sku }" - image-version = "${ var.image-version }" - admin-username = "${ var.admin-username }" + bastion_vm_size = "${ var.bastion_vm_size }" + image_publisher = "${ var.image_publisher }" + image_offer = "${ var.image_offer }" + image_sku = "${ var.image_sku }" + image_version = "${ var.image_version }" + admin_username = "${ var.admin_username }" subnet-id = "${ module.vpc.subnet-id }" storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" storage-container = "${ azurerm_storage_container.cncf.name }" @@ -64,23 +64,23 @@ module "worker" { source = "./modules/worker" name = "${ var.name }" location = "${ var.location }" - admin-username = "${ var.admin-username }" - worker-node-count = "${ var.worker-node-count }" - worker-vm-size = "${ var.worker-vm-size }" - image-publisher = "${ var.image-publisher }" - image-offer = "${ var.image-offer }" - image-sku = "${ var.image-sku }" - image-version = "${ var.image-version }" + admin_username = "${ var.admin_username }" + worker_node_count = "${ var.worker_node_count }" + worker_vm_size = "${ var.worker_vm_size }" + image_publisher = "${ var.image_publisher }" + image_offer = "${ var.image_offer }" + image_sku = "${ var.image_sku }" + image_version = "${ var.image_version }" subnet-id = "${ module.vpc.subnet-id }" storage-account = "${ azurerm_storage_account.cncf.name }" storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" storage-container = "${ azurerm_storage_container.cncf.name }" availability-id = "${ azurerm_availability_set.cncf.id }" external-lb = "${ module.etcd.external-lb }" - cluster-domain = "${ var.cluster-domain }" - kubelet-image-url = "${ var.kubelet-image-url }" - kubelet-image-tag = "${ var.kubelet-image-tag }" - dns-service-ip = "${ var.dns-service-ip }" + cluster_domain = "${ var.cluster_domain }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + dns_service_ip = "${ var.dns_service_ip }" internal-tld = "${ var.internal-tld }" k8s-worker-tar = "${file("${ var.data-dir }/.cfssl/k8s-worker.tar")}" cloud-config = "${file("${ var.data-dir }/azure-config.json")}" diff --git a/azure/modules/bastion/input.tf b/azure/modules/bastion/input.tf index 7cafb1a..a8af044 100644 --- a/azure/modules/bastion/input.tf +++ b/azure/modules/bastion/input.tf @@ -1,11 +1,11 @@ variable "name" {} variable "location" {} -variable "bastion-vm-size" {} -variable "image-publisher" {} -variable "image-offer" {} -variable "image-sku" {} -variable "image-version" {} -variable "admin-username" {} +variable "bastion_vm_size" {} +variable "image_publisher" {} +variable "image_offer" {} +variable "image_sku" {} +variable "image_version" {} +variable "admin_username" {} variable "internal-tld" {} variable "subnet-id" {} variable "availability-id" {} diff --git a/azure/modules/bastion/node.tf b/azure/modules/bastion/node.tf index 6611d79..543dbc2 100644 --- a/azure/modules/bastion/node.tf +++ b/azure/modules/bastion/node.tf @@ -25,13 +25,13 @@ resource "azurerm_virtual_machine" "cncf" { availability_set_id = "${ var.availability-id }" resource_group_name = "${ var.name }" network_interface_ids = ["${azurerm_network_interface.cncf.id}"] - vm_size = "${ var.bastion-vm-size }" + vm_size = "${ var.bastion_vm_size }" storage_image_reference { - publisher = "${ var.image-publisher }" - offer = "${ var.image-offer }" - sku = "${ var.image-sku }" - version = "${ var.image-version}" + publisher = "${ var.image_publisher }" + offer = "${ var.image_offer }" + sku = "${ var.image_sku }" + version = "${ var.image_version}" } storage_os_disk { @@ -43,7 +43,7 @@ resource "azurerm_virtual_machine" "cncf" { os_profile { computer_name = "hostname" - admin_username = "${ var.admin-username }" + admin_username = "${ var.admin_username }" admin_password = "Password1234!" custom_data = "${ data.template_file.user-data.rendered }" #custom_data = "${file("${path.module}/user-data2.yml")}" @@ -52,7 +52,7 @@ resource "azurerm_virtual_machine" "cncf" { os_profile_linux_config { disable_password_authentication = true ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" + path = "/home/${ var.admin_username }/.ssh/authorized_keys" key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" } } diff --git a/azure/modules/etcd/.#cloud-config.tf b/azure/modules/etcd/.#cloud-config.tf deleted file mode 120000 index 47d290a..0000000 --- a/azure/modules/etcd/.#cloud-config.tf +++ /dev/null @@ -1 +0,0 @@ -dlx@W541.15790:1490082843 \ No newline at end of file diff --git a/azure/modules/etcd/cloud-config.tf b/azure/modules/etcd/cloud-config.tf index 4dbb095..291216f 100644 --- a/azure/modules/etcd/cloud-config.tf +++ b/azure/modules/etcd/cloud-config.tf @@ -1,21 +1,21 @@ data "template_file" "cloud-config" { - count = "${ var.master-node-count }" + count = "${ var.master_node_count }" template = "${ file( "${ path.module }/cloud-config.yml" )}" vars { # bucket = "${ var.bucket-prefix }" - cluster-domain = "${ var.cluster-domain }" + cluster_domain = "${ var.cluster_domain }" cluster-token = "etcd-cluster-${ var.name }" - dns-service-ip = "${ var.dns-service-ip }" + dns_service_ip = "${ var.dns_service_ip }" etc-tar = "/manifests/etc.tar" fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" hostname = "etcd${ count.index + 1 }" - kubelet-image-url = "${ var.kubelet-image-url }" - kubelet-image-tag = "${ var.kubelet-image-tag }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" internal-tld = "${ var.internal-tld }" - pod-cidr = "${ var.pod-cidr }" + pod_cidr = "${ var.pod_cidr }" location = "${ var.location }" - service-cidr = "${ var.service-cidr }" + service_cidr = "${ var.service_cidr }" k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" cloud-config = "${ base64encode(var.cloud-config) }" diff --git a/azure/modules/etcd/cloud-config.yml b/azure/modules/etcd/cloud-config.yml index 3f67928..aa92bc4 100644 --- a/azure/modules/etcd/cloud-config.yml +++ b/azure/modules/etcd/cloud-config.yml @@ -52,8 +52,8 @@ coreos: [Unit] ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper [Service] - Environment="KUBELET_IMAGE_URL=${ kubelet-image-url }" - Environment="KUBELET_IMAGE_TAG=${ kubelet-image-tag }" + Environment="KUBELET_IMAGE_URL=${ kubelet_image_url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet_image_tag }" Environment="RKT_OPTS=\ --volume dns,kind=host,source=/etc/resolv.conf \ --mount volume=dns,target=/etc/resolv.conf \ @@ -74,8 +74,8 @@ coreos: --api-servers=http://127.0.0.1:8080 \ --cloud-provider=azure \ --cloud-config=/etc/kubernetes/ssl/azure-config.json \ - --cluster-dns=${ dns-service-ip } \ - --cluster-domain=${ cluster-domain } \ + --cluster-dns=${ dns_service_ip } \ + --cluster_domain=${ cluster_domain } \ --config=/etc/kubernetes/manifests \ --register-schedulable=false Restart=always @@ -105,7 +105,7 @@ write-files: hostNetwork: true containers: - name: kube-apiserver - image: ${ kubelet-image-url }:${ kubelet-image-tag } + image: ${ kubelet_image_url }:${ kubelet_image_tag } command: - /hyperkube - apiserver @@ -123,7 +123,7 @@ write-files: - --insecure-bind-address=0.0.0.0 - --secure-port=443 - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --service-cluster-ip-range=${ service-cidr } + - --service-cluster-ip-range=${ service_cidr } - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - --v=2 @@ -167,7 +167,7 @@ write-files: hostNetwork: true containers: - name: kube-controller-manager - image: ${ kubelet-image-url }:${ kubelet-image-tag } + image: ${ kubelet_image_url }:${ kubelet_image_tag } command: - /hyperkube - controller-manager @@ -213,7 +213,7 @@ write-files: hostNetwork: true containers: - name: kube-proxy - image: ${ kubelet-image-url }:${ kubelet-image-tag } + image: ${ kubelet_image_url }:${ kubelet_image_tag } command: - /hyperkube - proxy @@ -241,7 +241,7 @@ write-files: hostNetwork: true containers: - name: kube-scheduler - image: ${ kubelet-image-url }:${ kubelet-image-tag } + image: ${ kubelet_image_url }:${ kubelet_image_tag } command: - /hyperkube - scheduler diff --git a/azure/modules/etcd/input.tf b/azure/modules/etcd/input.tf index 318efaf..b1e52fe 100644 --- a/azure/modules/etcd/input.tf +++ b/azure/modules/etcd/input.tf @@ -1,25 +1,25 @@ variable "location" {} variable "subnet-id" {} variable "name" {} -variable "master-vm-size" {} -variable "master-node-count" {} -variable "image-publisher" {} -variable "image-offer" {} -variable "image-sku" {} -variable "image-version" {} +variable "master_vm_size" {} +variable "master_node_count" {} +variable "image_publisher" {} +variable "image_offer" {} +variable "image_sku" {} +variable "image_version" {} variable "availability-id" {} variable "storage-account" {} variable "storage-primary-endpoint" {} variable "storage-container" {} variable "k8s-apiserver-tar" {} -variable "cluster-domain" {} -variable "dns-service-ip" {} +variable "cluster_domain" {} +variable "dns_service_ip" {} variable "internal-tld" {} -variable "pod-cidr" {} -variable "service-cidr" {} -variable "admin-username" {} -variable "kubelet-image-url" {} -variable "kubelet-image-tag" {} +variable "pod_cidr" {} +variable "service_cidr" {} +variable "admin_username" {} +variable "kubelet_image_url" {} +variable "kubelet_image_tag" {} variable "cloud-config" {} # variable "etcd-security-group-id" {} diff --git a/azure/modules/etcd/nodes.tf b/azure/modules/etcd/nodes.tf index b96a4fc..8001df9 100644 --- a/azure/modules/etcd/nodes.tf +++ b/azure/modules/etcd/nodes.tf @@ -1,5 +1,5 @@ resource "azurerm_network_interface" "cncf" { - count = "${ var.master-node-count }" + count = "${ var.master_node_count }" name = "etcd-interface${ count.index + 1 }" location = "${ var.location }" resource_group_name = "${ var.name }" @@ -14,19 +14,19 @@ resource "azurerm_network_interface" "cncf" { } resource "azurerm_virtual_machine" "cncf" { - count = "${ var.master-node-count }" + count = "${ var.master_node_count }" name = "etcd-master${ count.index + 1 }" location = "${ var.location }" availability_set_id = "${ var.availability-id }" resource_group_name = "${ var.name }" network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] - vm_size = "${ var.master-vm-size }" + vm_size = "${ var.master_vm_size }" storage_image_reference { - publisher = "${ var.image-publisher }" - offer = "${ var.image-offer }" - sku = "${ var.image-sku }" - version = "${ var.image-version}" + publisher = "${ var.image_publisher }" + offer = "${ var.image_offer }" + sku = "${ var.image_sku }" + version = "${ var.image_version}" } storage_os_disk { @@ -38,7 +38,7 @@ resource "azurerm_virtual_machine" "cncf" { os_profile { computer_name = "etcd-master${ count.index + 1 }" - admin_username = "${ var.admin-username }" + admin_username = "${ var.admin_username }" admin_password = "Password1234!" custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" } @@ -46,7 +46,7 @@ resource "azurerm_virtual_machine" "cncf" { os_profile_linux_config { disable_password_authentication = true ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" + path = "/home/${ var.admin_username }/.ssh/authorized_keys" key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" } } diff --git a/azure/modules/worker/cloud-config.tf b/azure/modules/worker/cloud-config.tf index 76fa63d..ab450e5 100644 --- a/azure/modules/worker/cloud-config.tf +++ b/azure/modules/worker/cloud-config.tf @@ -2,10 +2,10 @@ data "template_file" "cloud-config" { template = "${ file( "${ path.module }/cloud-config.yml" )}" vars { - cluster-domain = "${ var.cluster-domain }" - dns-service-ip = "${ var.dns-service-ip }" - kubelet-image-url = "${ var.kubelet-image-url }" - kubelet-image-tag = "${ var.kubelet-image-tag }" + cluster_domain = "${ var.cluster_domain }" + dns_service_ip = "${ var.dns_service_ip }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" internal-tld = "${ var.internal-tld }" location = "${ var.location }" k8s-worker-tar = "${ base64encode(var.k8s-worker-tar) }" diff --git a/azure/modules/worker/cloud-config.yml b/azure/modules/worker/cloud-config.yml index 36d1937..70a1336 100644 --- a/azure/modules/worker/cloud-config.yml +++ b/azure/modules/worker/cloud-config.yml @@ -32,8 +32,8 @@ coreos: [Unit] ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper [Service] - Environment="KUBELET_IMAGE_URL=${ kubelet-image-url }" - Environment="KUBELET_IMAGE_TAG=${ kubelet-image-tag }" + Environment="KUBELET_IMAGE_URL=${ kubelet_image_url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet_image_tag }" Environment="RKT_OPTS=\ --volume dns,kind=host,source=/etc/resolv.conf \ --mount volume=dns,target=/etc/resolv.conf \ @@ -54,8 +54,8 @@ coreos: --api-servers=http://master.${ internal-tld }:8080 \ --cloud-provider=azure \ --cloud-config=/etc/kubernetes/ssl/azure-config.json \ - --cluster-dns=${ dns-service-ip } \ - --cluster-domain=${ cluster-domain } \ + --cluster-dns=${ dns_service_ip } \ + --cluster_domain=${ cluster_domain } \ --config=/etc/kubernetes/manifests \ --kubeconfig=/etc/kubernetes/kubeconfig.yml \ --register-node=true \ @@ -108,7 +108,7 @@ write-files: hostNetwork: true containers: - name: kube-proxy - image: ${ kubelet-image-url }:${ kubelet-image-tag } + image: ${ kubelet_image_url }:${ kubelet_image_tag } command: - /hyperkube - proxy diff --git a/azure/modules/worker/input.tf b/azure/modules/worker/input.tf index e5d9d19..999db92 100644 --- a/azure/modules/worker/input.tf +++ b/azure/modules/worker/input.tf @@ -1,22 +1,22 @@ variable "location" {} variable "subnet-id" {} variable "name" {} -variable "worker-vm-size" {} -variable "worker-node-count" {} -variable "image-publisher" {} -variable "image-offer" {} -variable "image-sku" {} -variable "image-version" {} +variable "worker_vm_size" {} +variable "worker_node_count" {} +variable "image_publisher" {} +variable "image_offer" {} +variable "image_sku" {} +variable "image_version" {} variable "storage-account" {} variable "storage-primary-endpoint" {} variable "storage-container" {} variable "availability-id" {} variable "external-lb" {} -variable "cluster-domain" {} -variable "dns-service-ip" {} +variable "cluster_domain" {} +variable "dns_service_ip" {} variable "internal-tld" {} -variable "admin-username" {} +variable "admin_username" {} variable "k8s-worker-tar" {} -variable "kubelet-image-url" {} -variable "kubelet-image-tag" {} +variable "kubelet_image_url" {} +variable "kubelet_image_tag" {} variable "cloud-config" {} diff --git a/azure/modules/worker/nodes.tf b/azure/modules/worker/nodes.tf index 42bde3f..9cd414d 100644 --- a/azure/modules/worker/nodes.tf +++ b/azure/modules/worker/nodes.tf @@ -1,5 +1,5 @@ resource "azurerm_network_interface" "cncf" { - count = "${ var.worker-node-count }" + count = "${ var.worker_node_count }" name = "worker-interface${ count.index + 1 }" location = "${ var.location }" resource_group_name = "${ var.name }" @@ -12,19 +12,19 @@ resource "azurerm_network_interface" "cncf" { } resource "azurerm_virtual_machine" "cncf" { - count = "${ var.worker-node-count }" + count = "${ var.worker_node_count }" name = "worker-node${ count.index + 1 }" location = "${ var.location }" availability_set_id = "${ var.availability-id }" resource_group_name = "${ var.name }" network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] - vm_size = "${ var.worker-vm-size }" + vm_size = "${ var.worker_vm_size }" storage_image_reference { - publisher = "${ var.image-publisher }" - offer = "${ var.image-offer }" - sku = "${ var.image-sku }" - version = "${ var.image-version}" + publisher = "${ var.image_publisher }" + offer = "${ var.image_offer }" + sku = "${ var.image_sku }" + version = "${ var.image_version}" } storage_os_disk { @@ -36,7 +36,7 @@ resource "azurerm_virtual_machine" "cncf" { os_profile { computer_name = "worker-node${ count.index + 1 }" - admin_username = "${ var.admin-username }" + admin_username = "${ var.admin_username }" admin_password = "Password1234!" custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" } @@ -44,7 +44,7 @@ resource "azurerm_virtual_machine" "cncf" { os_profile_linux_config { disable_password_authentication = true ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" + path = "/home/${ var.admin_username }/.ssh/authorized_keys" key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" } } @@ -65,7 +65,7 @@ resource "azurerm_virtual_machine_scale_set" "cncf" { os_profile { computer_name_prefix = "worker" - admin_username = "${ var.admin-username }" + admin_username = "${ var.admin_username }" admin_password = "Password1234" custom_data = "${ data.template_file.cloud-config.rendered }" } @@ -73,7 +73,7 @@ resource "azurerm_virtual_machine_scale_set" "cncf" { os_profile_linux_config { disable_password_authentication = true ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" + path = "/home/${ var.admin_username }/.ssh/authorized_keys" key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" } } diff --git a/azure/output.tf b/azure/output.tf index 0ea809d..985606f 100644 --- a/azure/output.tf +++ b/azure/output.tf @@ -4,13 +4,13 @@ output "bastion-fqdn" { value = "${ module.bastion.bastion-fqdn}" } output "k8s-admin" { value = "${ k8s-admin}"} # fixme for use outside container output "ssh-key-setup" { value = "eval $(ssh-agent) ; ssh-add /cncf/data/.ssh/id_rsa"} -output "ssh-via-bastion" { value = "ssh -At ${ var.admin-username }@${ module.bastion.bastion-fqdn } ssh ${ var.admin-username }@master1.cncf.demo"} +output "ssh-via-bastion" { value = "ssh -At ${ var.admin_username }@${ module.bastion.bastion-fqdn } ssh ${ var.admin_username }@master1.cncf.demo"} #output "availability-id" { value = "${ azurerm_availability_set.cncf.id }" } #output "azs" { value = "${ var.aws["azs"] }" } #output "bastion-ip" { value = "${ module.bastion.ip }" } -#output "cluster-domain" { value = "${ var.cluster-domain }" } -#output "dns-service-ip" { value = "${ var.dns-service-ip }" } +#output "cluster_domain" { value = "${ var.cluster_domain }" } +#output "dns_service_ip" { value = "${ var.dns_service_ip }" } #output "etcd1-ip" { value = "${ element( split(",", var.etcd-ips), 0 ) }" } #output "external-elb" { value = "${ module.etcd.external-elb }" } #output "internal-tld" { value = "${ var.internal-tld }" } diff --git a/azure/readme.org b/azure/readme.org index 4cec32b..4f8a40a 100644 --- a/azure/readme.org +++ b/azure/readme.org @@ -62,17 +62,17 @@ Adding var-name=value to ./data/terraform.tfvars will allow you to override many name= "azure" location= "westus" internal-tld= "cncf.demo" -master-node-count= "3" -worker-node-count= "3" -master-vm-size= "Standard_A2" -worker-vm-size= "Standard_A2" -bastion-vm-size= "Standard_A2" -kubelet-image-url= "quay.io/coreos/hyperkube" -kubelet-image-tag= "v1.4.7_coreos.0" -image-publisher= "CoreOS" -image-offer= "CoreOS" -image-sku= "Stable" -image-version= "1298.6.0" +master_node_count= "3" +worker_node_count= "3" +master_vm_size= "Standard_A2" +worker_vm_size= "Standard_A2" +bastion_vm_size= "Standard_A2" +kubelet_image_url= "quay.io/coreos/hyperkube" +kubelet_image_tag= "v1.4.7_coreos.0" +image_publisher= "CoreOS" +image_offer= "CoreOS" +image_sku= "Stable" +image_version= "1298.6.0" #+END_SRC * Next steps From e69f66427e2589d203bce69f928b50cc435b99a3 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 10 Apr 2017 11:23:11 +1200 Subject: [PATCH 011/149] Continued refactor for - to _ --- azure/cert.tf | 6 +++--- azure/modules.tf | 8 ++++---- azure/modules/bastion/input.tf | 2 +- azure/modules/bastion/node.tf | 2 +- azure/modules/bastion/user-data.yml | 2 +- azure/modules/dns/dns.tf | 14 +++++++------- azure/modules/dns/input.tf | 2 +- azure/modules/etcd/cloud-config.tf | 4 ++-- azure/modules/etcd/cloud-config.yml | 4 ++-- azure/modules/etcd/input.tf | 2 +- azure/modules/worker/cloud-config.tf | 2 +- azure/modules/worker/cloud-config.yml | 6 +++--- azure/modules/worker/input.tf | 2 +- azure/output.tf | 2 +- azure/readme.org | 2 +- 15 files changed, 30 insertions(+), 30 deletions(-) diff --git a/azure/cert.tf b/azure/cert.tf index 0e7bc50..16835e2 100644 --- a/azure/cert.tf +++ b/azure/cert.tf @@ -4,9 +4,9 @@ resource "null_resource" "ssl_gen" { provisioner "local-exec" { command = < Date: Thu, 13 Apr 2017 11:55:07 +1200 Subject: [PATCH 012/149] Add Google, Azure, and AWS tools to Dockerfile --- Dockerfile | 26 +++++++++++++++++---- aws/aws.tf | 52 +++++------------------------------------ aws/cleanup.tf | 23 ++++++++++++++++++ aws/keypair.tf | 17 ++++++++++++++ aws/modules/etcd/elb.tf | 2 +- 5 files changed, 68 insertions(+), 52 deletions(-) create mode 100644 aws/cleanup.tf create mode 100644 aws/keypair.tf diff --git a/Dockerfile b/Dockerfile index c23fadc..ec68df5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,32 @@ FROM golang:alpine MAINTAINER "Denver Williams " -ENV TERRAFORM_VERSION=0.9.0-beta2 ENV KUBECTL_VERSION=v1.5.2 +ENV GCLOUD_VERSION=150.0.0 +ENV AWSCLI_VERSION=1.11.75 +ENV AZURECLI_VERSION=2.0.2 +ENV TERRAFORM_VERSION=0.9.0-beta2 ENV ARC=amd64 ENV AWS_CONFIG_FILE=/cncf/data/awsconfig ENV KUBECONFIG=/cncf/data/kubeconfig -# Install AWS CLI + Deps -RUN apk add --update git bash util-linux wget tar curl build-base jq openssh bind-tools && \ + +# Install AWS / AZURE CLI Deps +RUN apk update +RUN apk add --update git bash util-linux wget tar curl build-base jq \ + py-pip groff less openssh bind-tools python python-dev libffi-dev openssl-dev + +RUN pip install azure-cli==${AZURECLI_VERSION} +RUN pip install awscli==${AWSCLI_VERSION} + +RUN apk --purge -v del py-pip && \ rm /var/cache/apk/* +#Install Google Cloud SDK +RUN wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${GCLOUD_VERSION}-linux-x86.tar.gz && \ +tar xvfz google-cloud-sdk-${GCLOUD_VERSION}-linux-x86.tar.gz && \ +./google-cloud-sdk/install.sh -q + #Install Kubectl -RUN wget -O /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$KUBECTL_VERSION/bin/linux/$ARC/kubectl && \ +RUN wget -O /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/$ARC/kubectl && \ chmod +x /usr/local/bin/kubectl # Install Terraform @@ -19,8 +35,8 @@ RUN unzip terraform*.zip -d /usr/bin # Install CFSSL RUN go get -u github.com/cloudflare/cfssl/cmd/cfssl && \ -#Add Terraform Modules go get -u github.com/cloudflare/cfssl/cmd/... +#Add Terraform Modules COPY aws /aws/ COPY azure /azure/ diff --git a/aws/aws.tf b/aws/aws.tf index 02855b6..a1d5306 100644 --- a/aws/aws.tf +++ b/aws/aws.tf @@ -1,46 +1,6 @@ -provider "aws" { region = "${ var.aws_region }" } - -# Add AWS Keypair -resource "null_resource" "aws_keypair" { - - provisioner "local-exec" { - command = < ${ var.data_dir }/${ var.aws_key_name }.pem -chmod 400 ${ var.data_dir }/${ var.aws_key_name }.pem -EOF - } - -} - -resource "null_resource" "dummy_dependency2" { - depends_on = [ "null_resource.aws_keypair" ] -} - - -# Clean-up Destroy -resource "null_resource" "cleanup" { - - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = < ${ var.data_dir }/${ var.aws_key_name }.pem +chmod 400 ${ var.data_dir }/${ var.aws_key_name }.pem +EOF + } +} + +resource "null_resource" "dummy_dependency2" { + depends_on = [ "null_resource.aws_keypair" ] +} diff --git a/aws/modules/etcd/elb.tf b/aws/modules/etcd/elb.tf index b102bc8..2b25ffe 100644 --- a/aws/modules/etcd/elb.tf +++ b/aws/modules/etcd/elb.tf @@ -1,5 +1,5 @@ resource "aws_elb" "external" { - name = "kz8s-apiserver-${replace(var.name, "/(.{0,17})(.*)/", "$1")}" + name = "${replace(var.name, "/(.{0,17})(.*)/", "$1")}-apiserver" cross_zone_load_balancing = false From 96c070e5da4e75d3b553cff86e536f18a33648e1 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Fri, 14 Apr 2017 12:02:15 +1200 Subject: [PATCH 013/149] continuing aws+azure refactor --- aws/modules.tf | 28 ++++++++++++++++++++-------- aws/modules/etcd/cloud-config.tf | 4 ++-- aws/modules/etcd/cloud-config.yml | 27 ++++++++++++++++++++++----- aws/modules/etcd/ec2.tf | 1 + aws/modules/etcd/elb.tf | 3 +-- aws/modules/etcd/input.tf | 2 ++ aws/modules/s3/input.tf | 6 +----- aws/modules/s3/output.tf | 2 +- aws/modules/s3/s3-cp | 6 +----- aws/modules/s3/s3.tf | 4 ---- aws/modules/worker/cloud-config.tf | 3 ++- aws/modules/worker/cloud-config.yml | 27 ++++++++++++++++++++++----- aws/modules/worker/ec2.tf | 2 +- aws/modules/worker/input.tf | 3 ++- 14 files changed, 78 insertions(+), 40 deletions(-) diff --git a/aws/modules.tf b/aws/modules.tf index 00d0495..4cfe4d7 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -1,3 +1,13 @@ +module "s3" { + source = "./modules/s3" + depends-id = "${ module.vpc.depends-id }" + s3_bucket= "${ var.name }-demobucket" + name = "${ var.name }" + region = "${ var.aws_region }" + data_dir = "${ var.data_dir }" +} + + module "vpc" { source = "./modules/vpc" depends-id = "" @@ -18,13 +28,12 @@ module "security" { vpc-id = "${ module.vpc.id }" } -# module "iam" { -# source = "./modules/iam" -# depends-id = "${ module.s3.depends-id }" - -# s3_bucket = "${ var.s3_bucket }" -# name = "${ var.name }" -# } +module "iam" { + source = "./modules/iam" + depends-id = "${ module.s3.depends-id }" + s3_bucket = "${ module.s3.bucket }" + name = "${ var.name }" +} module "route53" { source = "./modules/route53" @@ -38,6 +47,8 @@ module "route53" { module "etcd" { source = "./modules/etcd" depends-id = "${ module.route53.depends-id }" + s3_bucket = "${ module.s3.bucket }" + instance-profile-name = "${ module.iam.instance-profile-name-master }" ami-id = "${ var.aws_image_ami }" cluster_domain = "${ var.cluster_domain }" @@ -77,6 +88,7 @@ module "bastion" { module "worker" { source = "./modules/worker" depends-id = "${ module.route53.depends-id }" + s3_bucket = "${ module.s3.bucket }" ami-id = "${ var.aws_image_ami }" capacity = { @@ -84,6 +96,7 @@ module "worker" { max = 5 min = 3 } + instance-profile-name = "${ module.iam.instance-profile-name-worker }" cluster_domain = "${ var.cluster_domain }" kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" @@ -101,7 +114,6 @@ module "worker" { } vpc-id = "${ module.vpc.id }" worker-name = "general" - k8s-worker-tar = "${file("${ var.data_dir }/.cfssl/k8s-worker.tar.bz2")}" } module "kubeconfig" { diff --git a/aws/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf index 6431090..1349b9c 100644 --- a/aws/modules/etcd/cloud-config.tf +++ b/aws/modules/etcd/cloud-config.tf @@ -16,8 +16,8 @@ data "template_file" "cloud-config" { pod-ip-range = "${ var.pod-ip-range }" region = "${ var.region }" service-cluster-ip-range = "${ var.service-cluster-ip-range }" - ssl-tar = "ssl/k8s-apiserver.tar" - k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" + ssl_tar = "ssl/k8s-apiserver.tar.bz2" + bucket = "${var.s3_bucket}" } } diff --git a/aws/modules/etcd/cloud-config.yml b/aws/modules/etcd/cloud-config.yml index 92cf3a3..7f33434 100644 --- a/aws/modules/etcd/cloud-config.yml +++ b/aws/modules/etcd/cloud-config.yml @@ -57,11 +57,32 @@ coreos: [Service] Environment="DOCKER_OPTS=--storage-driver=overlay" + - name: s3-get-presigned-url.service + command: start + content: | + [Unit] + After=network-online.target + Description=Install s3-get-presigned-url + Requires=network-online.target + [Service] + ExecStartPre=-/usr/bin/mkdir -p /opt/bin + ExecStart=/usr/bin/curl -L -o /opt/bin/s3-get-presigned-url \ + https://github.com/kz8s/s3-get-presigned-url/releases/download/v0.1/s3-get-presigned-url_linux_amd64 + ExecStart=/usr/bin/chmod +x /opt/bin/s3-get-presigned-url + RemainAfterExit=yes + Type=oneshot + - name: get-ssl.service command: start content: | + [Unit] + After=s3-get-presigned-url.service + Description=Get ssl artifacts from s3 bucket using IAM role + Requires=s3-get-presigned-url.service [Service] - ExecStart=/bin/sh -c "tar xvj -f /etc/kubernetes/ssl/k8s-apiserver.tar.bz2 -C /etc/kubernetes/ssl/" + ExecStartPre=-/usr/bin/mkdir -p /etc/kubernetes/ssl + ExecStart=/bin/sh -c "/usr/bin/curl $(/opt/bin/s3-get-presigned-url \ + ${ region } ${ bucket } ${ ssl_tar }) | tar xvj -C /etc/kubernetes/ssl/" RemainAfterExit=yes Type=oneshot @@ -285,7 +306,3 @@ write-files: delaycompress copytruncate } - - - encoding: b64 - content: ${ k8s-apiserver-tar } - path: /etc/kubernetes/ssl/k8s-apiserver.tar.bz2 diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 32fbdf9..50335bd 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -3,6 +3,7 @@ resource "aws_instance" "etcd" { ami = "${ var.ami-id }" associate_public_ip_address = false + iam_instance_profile = "${ var.instance-profile-name }" instance_type = "${ var.instance-type }" key_name = "${ var.key-name }" private_ip = "${ element(split(",", var.etcd_ips), count.index) }" diff --git a/aws/modules/etcd/elb.tf b/aws/modules/etcd/elb.tf index 2b25ffe..078d576 100644 --- a/aws/modules/etcd/elb.tf +++ b/aws/modules/etcd/elb.tf @@ -1,5 +1,5 @@ resource "aws_elb" "external" { - name = "${replace(var.name, "/(.{0,17})(.*)/", "$1")}-apiserver" + name = "kz8s-apiserver-${replace(var.name, "/(.{0,17})(.*)/", "$1")}" cross_zone_load_balancing = false @@ -29,7 +29,6 @@ resource "aws_elb" "external" { kz8s = "${ var.name }" Name = "kz8s-apiserver" role = "apiserver" - version = "${ var.kubelet_version }" visibility = "public" KubernetesCluster = "${ var.name }" } diff --git a/aws/modules/etcd/input.tf b/aws/modules/etcd/input.tf index e0ac8c9..a5cf789 100644 --- a/aws/modules/etcd/input.tf +++ b/aws/modules/etcd/input.tf @@ -18,3 +18,5 @@ variable "subnet-ids-private" {} variable "subnet-ids-public" {} variable "vpc-id" {} variable "k8s-apiserver-tar" {} +variable "s3_bucket" {} +variable "instance-profile-name" {} diff --git a/aws/modules/s3/input.tf b/aws/modules/s3/input.tf index eb97fd2..ef527a0 100644 --- a/aws/modules/s3/input.tf +++ b/aws/modules/s3/input.tf @@ -1,9 +1,5 @@ variable "name" {} variable "region" {} -variable "s3_bucket" { default = "${ var.name}-demobucket"} -variable "kubelet_aci" {} -variable "kubelet_version" {} +variable "s3_bucket" {} variable "depends-id" {} -variable "internal_tld" {} -variable "service-cluster-ip-range" {} variable "data_dir" {} diff --git a/aws/modules/s3/output.tf b/aws/modules/s3/output.tf index 291f719..7748c2f 100644 --- a/aws/modules/s3/output.tf +++ b/aws/modules/s3/output.tf @@ -1,2 +1,2 @@ -output "s3_bucket" { value = "${ var.s3_bucket }" } +output "bucket" { value = "${ aws_s3_bucket.ssl.bucket }" } output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } diff --git a/aws/modules/s3/s3-cp b/aws/modules/s3/s3-cp index 75b48e8..eb3ccd4 100755 --- a/aws/modules/s3/s3-cp +++ b/aws/modules/s3/s3-cp @@ -30,15 +30,11 @@ BUCKET=$1 [ -z "$BUCKET" ] && usage # assert environment variables -[ -z "$HYPERKUBE" ] && usage -[ -z "$INTERNAL_TLD" ] && usage [ -z "$REGION" ] && usage [ -z "$DIR_SSL"] && usage # print environment and parameter echo "❤ s3-cp using these settings" -echo envvar HYPERKUBE=${HYPERKUBE} -echo envvar INTERNAL_TLD=${INTERNAL_TLD} echo envvar REGION=${REGION} echo envvar DIR_SSL=${DIR_SSL} echo parameter BUCKET=${BUCKET} @@ -55,6 +51,6 @@ set -o pipefail # cp tls assets echo "❤ Copy tls assets to s3 ${BUCKET}" -aws s3 cp "$DIR_SSL" s3://${BUCKET}/ssl --recursive --exclude "*" --include "*.tar" \ +aws s3 cp "$DIR_SSL" s3://${BUCKET}/ssl --recursive --exclude "*" --include "*.tar.bz2" \ --region ${REGION} echo "✓ tls copy success" diff --git a/aws/modules/s3/s3.tf b/aws/modules/s3/s3.tf index 64bd13d..6cdc8ba 100644 --- a/aws/modules/s3/s3.tf +++ b/aws/modules/s3/s3.tf @@ -7,15 +7,11 @@ resource "aws_s3_bucket" "ssl" { builtWith = "terraform" KubernetesCluster = "${ var.name }" Name = "kz8s-${ var.name }" - version = "${ var.kubelet_version }" } provisioner "local-exec" { command = < Date: Fri, 14 Apr 2017 12:05:20 +1200 Subject: [PATCH 014/149] update .gitignore --- .gitignore | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 498c22b..f9320f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -myazure.publishsettings -*.env .DS_Store *~ *.swp @@ -9,4 +7,9 @@ myazure.publishsettings *.zip *.pyc *.dist-info/ -api/_tes* \ No newline at end of file +data/ +terraform.tfstate +terraform.tfstate.backup +tmp/ +*.env +.terraform/ From b49edea0ea098fee7663e3ce8d29545df888a32c Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 14:42:41 +1200 Subject: [PATCH 015/149] Write Master Certs to Disk using cloud-init and Encode with base64+gzip to bypass AWS 16k Cloud-init Limit --- Dockerfile | 4 ++ aws/modules.tf | 42 +++++++++--------- aws/modules/etcd/cloud-config.tf | 31 +++++++++++++- aws/modules/etcd/cloud-config.yml | 66 ++++++++++++++--------------- aws/modules/etcd/ec2.tf | 2 +- aws/modules/etcd/input.tf | 10 +++-- aws/modules/worker/cloud-config.tf | 2 +- aws/modules/worker/cloud-config.yml | 29 ------------- aws/modules/worker/ec2.tf | 2 +- aws/modules/worker/input.tf | 4 +- 10 files changed, 100 insertions(+), 92 deletions(-) diff --git a/Dockerfile b/Dockerfile index ec68df5..fb2b7e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,6 +36,10 @@ RUN unzip terraform*.zip -d /usr/bin # Install CFSSL RUN go get -u github.com/cloudflare/cfssl/cmd/cfssl && \ go get -u github.com/cloudflare/cfssl/cmd/... + +# Install Gzip+base64 Provider +RUN go get -u github.com/jakexks/terraform-provider-gzip + #Add Terraform Modules COPY aws /aws/ diff --git a/aws/modules.tf b/aws/modules.tf index 4cfe4d7..c0ba896 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -1,11 +1,11 @@ -module "s3" { - source = "./modules/s3" - depends-id = "${ module.vpc.depends-id }" - s3_bucket= "${ var.name }-demobucket" - name = "${ var.name }" - region = "${ var.aws_region }" - data_dir = "${ var.data_dir }" -} +# module "s3" { +# source = "./modules/s3" +# depends-id = "${ module.vpc.depends-id }" +# s3_bucket= "${ var.name }-demobucket" +# name = "${ var.name }" +# region = "${ var.aws_region }" +# data_dir = "${ var.data_dir }" +# } module "vpc" { @@ -28,12 +28,12 @@ module "security" { vpc-id = "${ module.vpc.id }" } -module "iam" { - source = "./modules/iam" - depends-id = "${ module.s3.depends-id }" - s3_bucket = "${ module.s3.bucket }" - name = "${ var.name }" -} +# module "iam" { +# source = "./modules/iam" +# depends-id = "${ module.s3.depends-id }" +# s3_bucket = "${ module.s3.bucket }" +# name = "${ var.name }" +# } module "route53" { source = "./modules/route53" @@ -47,8 +47,8 @@ module "route53" { module "etcd" { source = "./modules/etcd" depends-id = "${ module.route53.depends-id }" - s3_bucket = "${ module.s3.bucket }" - instance-profile-name = "${ module.iam.instance-profile-name-master }" + # s3_bucket = "${ module.s3.bucket }" + # instance-profile-name = "${ module.iam.instance-profile-name-master }" ami-id = "${ var.aws_image_ami }" cluster_domain = "${ var.cluster_domain }" @@ -68,7 +68,11 @@ module "etcd" { subnet-ids-private = "${ module.vpc.subnet-ids-private }" subnet-ids-public = "${ module.vpc.subnet-ids-public }" vpc-id = "${ module.vpc.id }" - k8s-apiserver-tar = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.tar.bz2")}" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s-etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" + k8s-etcd-key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" + k8s-apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" + k8s-apiserver-key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" } module "bastion" { @@ -88,7 +92,7 @@ module "bastion" { module "worker" { source = "./modules/worker" depends-id = "${ module.route53.depends-id }" - s3_bucket = "${ module.s3.bucket }" + # s3_bucket = "${ module.s3.bucket }" ami-id = "${ var.aws_image_ami }" capacity = { @@ -96,7 +100,7 @@ module "worker" { max = 5 min = 3 } - instance-profile-name = "${ module.iam.instance-profile-name-worker }" + # instance-profile-name = "${ module.iam.instance-profile-name-worker }" cluster_domain = "${ var.cluster_domain }" kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" diff --git a/aws/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf index 1349b9c..77e9cf9 100644 --- a/aws/modules/etcd/cloud-config.tf +++ b/aws/modules/etcd/cloud-config.tf @@ -1,3 +1,27 @@ +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s-etcd" { + input = "${ var.k8s-etcd }" +} + +resource "gzip_me" "k8s-etcd-key" { + input = "${ var.k8s-etcd-key }" +} + +resource "gzip_me" "k8s-apiserver" { + input = "${ var.k8s-apiserver }" +} + +resource "gzip_me" "k8s-apiserver-key" { + input = "${ var.k8s-apiserver-key }" +} + data "template_file" "cloud-config" { count = "${ length( split(",", var.etcd_ips) ) }" template = "${ file( "${ path.module }/cloud-config.yml" )}" @@ -16,8 +40,13 @@ data "template_file" "cloud-config" { pod-ip-range = "${ var.pod-ip-range }" region = "${ var.region }" service-cluster-ip-range = "${ var.service-cluster-ip-range }" + ca = "${ gzip_me.ca.output }" + k8s-etcd = "${ gzip_me.k8s-etcd.output }" + k8s-etcd-key = "${ gzip_me.k8s-etcd-key.output }" + k8s-apiserver = "${ gzip_me.k8s-apiserver.output }" + k8s-apiserver-key = "${ gzip_me.k8s-apiserver.output }" ssl_tar = "ssl/k8s-apiserver.tar.bz2" - bucket = "${var.s3_bucket}" + # bucket = "${var.s3_bucket}" } } diff --git a/aws/modules/etcd/cloud-config.yml b/aws/modules/etcd/cloud-config.yml index 7f33434..70d4b1f 100644 --- a/aws/modules/etcd/cloud-config.yml +++ b/aws/modules/etcd/cloud-config.yml @@ -23,12 +23,6 @@ coreos: units: - name: etcd2.service command: start - drop-ins: - - name: wait-for-certs.conf - content: | - [Unit] - After=get-ssl.service - Requires=get-ssl.service - name: flanneld.service command: start @@ -57,35 +51,6 @@ coreos: [Service] Environment="DOCKER_OPTS=--storage-driver=overlay" - - name: s3-get-presigned-url.service - command: start - content: | - [Unit] - After=network-online.target - Description=Install s3-get-presigned-url - Requires=network-online.target - [Service] - ExecStartPre=-/usr/bin/mkdir -p /opt/bin - ExecStart=/usr/bin/curl -L -o /opt/bin/s3-get-presigned-url \ - https://github.com/kz8s/s3-get-presigned-url/releases/download/v0.1/s3-get-presigned-url_linux_amd64 - ExecStart=/usr/bin/chmod +x /opt/bin/s3-get-presigned-url - RemainAfterExit=yes - Type=oneshot - - - name: get-ssl.service - command: start - content: | - [Unit] - After=s3-get-presigned-url.service - Description=Get ssl artifacts from s3 bucket using IAM role - Requires=s3-get-presigned-url.service - [Service] - ExecStartPre=-/usr/bin/mkdir -p /etc/kubernetes/ssl - ExecStart=/bin/sh -c "/usr/bin/curl $(/opt/bin/s3-get-presigned-url \ - ${ region } ${ bucket } ${ ssl_tar }) | tar xvj -C /etc/kubernetes/ssl/" - RemainAfterExit=yes - Type=oneshot - - name: kubelet.service command: start content: | @@ -306,3 +271,34 @@ write-files: delaycompress copytruncate } + + - path: /etc/kubernetes/ssl/ca.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ ca } + + - path: /etc/kubernetes/ssl/k8s-etcd.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-etcd } + + - path: /etc/kubernetes/ssl/k8s-etcd-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-etcd-key } + + - path: /etc/kubernetes/ssl/k8s-apiserver.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-apiserver } + + - path: /etc/kubernetes/ssl/k8s-apiserver-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-apiserver-key } + diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 50335bd..57407d3 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -3,7 +3,7 @@ resource "aws_instance" "etcd" { ami = "${ var.ami-id }" associate_public_ip_address = false - iam_instance_profile = "${ var.instance-profile-name }" + # iam_instance_profile = "${ var.instance-profile-name }" instance_type = "${ var.instance-type }" key_name = "${ var.key-name }" private_ip = "${ element(split(",", var.etcd_ips), count.index) }" diff --git a/aws/modules/etcd/input.tf b/aws/modules/etcd/input.tf index a5cf789..32e3bde 100644 --- a/aws/modules/etcd/input.tf +++ b/aws/modules/etcd/input.tf @@ -17,6 +17,10 @@ variable "service-cluster-ip-range" {} variable "subnet-ids-private" {} variable "subnet-ids-public" {} variable "vpc-id" {} -variable "k8s-apiserver-tar" {} -variable "s3_bucket" {} -variable "instance-profile-name" {} +variable "ca" {} +variable "k8s-etcd" {} +variable "k8s-etcd-key" {} +variable "k8s-apiserver" {} +variable "k8s-apiserver-key" {} +# variable "s3_bucket" {} +# variable "instance-profile-name" {} diff --git a/aws/modules/worker/cloud-config.tf b/aws/modules/worker/cloud-config.tf index 87ffd68..d433294 100644 --- a/aws/modules/worker/cloud-config.tf +++ b/aws/modules/worker/cloud-config.tf @@ -9,6 +9,6 @@ data "template_file" "cloud-config" { internal_tld = "${ var.internal_tld }" region = "${ var.region }" ssl_tar = "/ssl/k8s-worker.tar.bz2" - bucket = "${ var.s3_bucket }" + # bucket = "${ var.s3_bucket }" } } diff --git a/aws/modules/worker/cloud-config.yml b/aws/modules/worker/cloud-config.yml index 1413cd2..e6aba53 100644 --- a/aws/modules/worker/cloud-config.yml +++ b/aws/modules/worker/cloud-config.yml @@ -62,35 +62,6 @@ coreos: Restart=always RestartSec=10 - - name: s3-get-presigned-url.service - command: start - content: | - [Unit] - After=network-online.target - Description=Install s3-get-presigned-url - Requires=network-online.target - [Service] - ExecStartPre=-/usr/bin/mkdir -p /opt/bin - ExecStart=/usr/bin/curl -L -o /opt/bin/s3-get-presigned-url \ - https://github.com/kz8s/s3-get-presigned-url/releases/download/v0.1/s3-get-presigned-url_linux_amd64 - ExecStart=/usr/bin/chmod +x /opt/bin/s3-get-presigned-url - RemainAfterExit=yes - Type=oneshot - - - name: get-ssl.service - command: start - content: | - [Unit] - After=s3-get-presigned-url.service - Description=Get ssl artifacts from s3 bucket using IAM role - Requires=s3-get-presigned-url.service - [Service] - ExecStartPre=-/usr/bin/mkdir -p /etc/kubernetes/ssl - ExecStart=/bin/sh -c "/usr/bin/curl $(/opt/bin/s3-get-presigned-url \ - ${ region } ${ bucket } ${ ssl_tar }) | tar xvj -C /etc/kubernetes/ssl/" - RemainAfterExit=yes - Type=oneshot - - name: kubelet.service command: start content: | diff --git a/aws/modules/worker/ec2.tf b/aws/modules/worker/ec2.tf index eb09b88..3b995d0 100644 --- a/aws/modules/worker/ec2.tf +++ b/aws/modules/worker/ec2.tf @@ -4,7 +4,7 @@ resource "aws_launch_configuration" "worker" { volume_size = "${ var.volume_size["ebs"] }" volume_type = "gp2" } - iam_instance_profile = "${ var.instance-profile-name }" + # iam_instance_profile = "${ var.instance-profile-name }" image_id = "${ var.ami-id }" instance_type = "${ var.instance-type }" key_name = "${ var.key-name }" diff --git a/aws/modules/worker/input.tf b/aws/modules/worker/input.tf index 3882061..39ffa52 100644 --- a/aws/modules/worker/input.tf +++ b/aws/modules/worker/input.tf @@ -26,5 +26,5 @@ variable "volume_size" { } variable "vpc-id" {} variable "worker-name" {} -variable "instance-profile-name" {} -variable "s3_bucket" {} +# variable "instance-profile-name" {} +# variable "s3_bucket" {} From 24bc820ebd6dcefd69c6360d904af118cd51bdb0 Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 18:35:10 +1200 Subject: [PATCH 016/149] Write Terraformrc and Remove AWS Cred Gen --- entrypoint.sh | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index b0690d3..cc366f7 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -6,21 +6,11 @@ set -e RED='\033[0;31m' NC='\033[0m' # No Color -function write_aws_config { - # Only if AWS_*_vars exist - # AWS_CONFIG_FILE - # AWS_DEFAULT_OUTPUT - # AWS_SHARED_CREDENTIALS_FILE - # Write aws Creds if they don't exist - if [ -f /cncf/data/awsconfig ] ; then - echo "Creds Already Exist Don't Gen" - else - cat </cncf/data/awsconfig -[default] -output = ${AWS_DEFAULT_OUTPUT:-json} -region = ${AWS_DEFAULT_REGION:-ap-southeast-2} -aws_access_key_id = ${AWS_ACCESS_KEY_ID} -aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY} +function write_terraformrc { + cat <~/.terraformrc +providers { + gzip = "terraform-provider-gzip" +} EOF fi } @@ -29,7 +19,7 @@ export TF_VAR_name="$2" # Run CMD if [ "$1" = "aws-deploy" ] ; then - write_aws_config + write_terraformrc terraform get /deploy/aws && \ terraform apply -target null_resource.ssl_gen /deploy/aws && \ time terraform apply /deploy/aws && \ From 131747d76ea655940bf6bbd108f8e16d5d8c0bba Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 18:36:36 +1200 Subject: [PATCH 017/149] Add iam for Adding Instances to Kube-APIServer and Remove S3 Roles --- aws/modules.tf | 34 ++++++++++++++-------------------- aws/modules/iam/etcd.tf | 8 -------- aws/modules/iam/io.tf | 4 ++-- aws/modules/iam/worker.tf | 8 -------- 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/aws/modules.tf b/aws/modules.tf index c0ba896..fcf0ba1 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -1,13 +1,3 @@ -# module "s3" { -# source = "./modules/s3" -# depends-id = "${ module.vpc.depends-id }" -# s3_bucket= "${ var.name }-demobucket" -# name = "${ var.name }" -# region = "${ var.aws_region }" -# data_dir = "${ var.data_dir }" -# } - - module "vpc" { source = "./modules/vpc" depends-id = "" @@ -28,12 +18,14 @@ module "security" { vpc-id = "${ module.vpc.id }" } -# module "iam" { -# source = "./modules/iam" -# depends-id = "${ module.s3.depends-id }" -# s3_bucket = "${ module.s3.bucket }" -# name = "${ var.name }" -# } + +module "iam" { + source = "./modules/iam" + # depends-id = "${ module.s3.depends-id }" + # s3_bucket = "${ module.s3.bucket }" + name = "${ var.name }" +} + module "route53" { source = "./modules/route53" @@ -47,8 +39,7 @@ module "route53" { module "etcd" { source = "./modules/etcd" depends-id = "${ module.route53.depends-id }" - # s3_bucket = "${ module.s3.bucket }" - # instance-profile-name = "${ module.iam.instance-profile-name-master }" + instance-profile-name = "${ module.iam.instance-profile-name-master }" ami-id = "${ var.aws_image_ami }" cluster_domain = "${ var.cluster_domain }" @@ -92,7 +83,7 @@ module "bastion" { module "worker" { source = "./modules/worker" depends-id = "${ module.route53.depends-id }" - # s3_bucket = "${ module.s3.bucket }" + instance-profile-name = "${ module.iam.instance-profile-name-worker }" ami-id = "${ var.aws_image_ami }" capacity = { @@ -100,7 +91,6 @@ module "worker" { max = 5 min = 3 } - # instance-profile-name = "${ module.iam.instance-profile-name-worker }" cluster_domain = "${ var.cluster_domain }" kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" @@ -112,6 +102,10 @@ module "worker" { region = "${ var.aws_region }" security-group-id = "${ module.security.worker-id }" subnet-ids = "${ module.vpc.subnet-ids-private }" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s-worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" + k8s-worker-key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" + volume_size = { ebs = 250 root = 52 diff --git a/aws/modules/iam/etcd.tf b/aws/modules/iam/etcd.tf index df8bf45..b5c3046 100644 --- a/aws/modules/iam/etcd.tf +++ b/aws/modules/iam/etcd.tf @@ -30,14 +30,6 @@ resource "aws_iam_role_policy" "master" { { "Version": "2012-10-17", "Statement": [ - { - "Action": [ - "s3:List*", - "s3:Get*" - ], - "Effect": "Allow", - "Resource": [ "arn:aws:s3:::${ var.s3_bucket }/*" ] - }, { "Action": [ "ec2:*", diff --git a/aws/modules/iam/io.tf b/aws/modules/iam/io.tf index 47ac64f..1e7512c 100644 --- a/aws/modules/iam/io.tf +++ b/aws/modules/iam/io.tf @@ -1,5 +1,5 @@ -variable "s3_bucket" {} -variable "depends-id" {} +# variable "s3_bucket" {} +# variable "depends-id" {} variable "name" {} output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } diff --git a/aws/modules/iam/worker.tf b/aws/modules/iam/worker.tf index c2563ac..11227e5 100644 --- a/aws/modules/iam/worker.tf +++ b/aws/modules/iam/worker.tf @@ -30,14 +30,6 @@ resource "aws_iam_role_policy" "worker" { { "Version": "2012-10-17", "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:List*", - "s3:Get*" - ], - "Resource": [ "arn:aws:s3:::${ var.s3_bucket }/*" ] - }, { "Effect": "Allow", "Action": [ From 0cf0d686a55f44c7894e23c7625cfe0fb047a294 Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 18:37:57 +1200 Subject: [PATCH 018/149] Add gzip+base64 encoding for writing worker certs to Disk for 16K Cloud-init limit --- aws/modules/worker/cloud-config.tf | 21 +++++++++++++++++++-- aws/modules/worker/cloud-config.yml | 18 ++++++++++++++++++ aws/modules/worker/ec2.tf | 2 +- aws/modules/worker/input.tf | 5 ++++- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/aws/modules/worker/cloud-config.tf b/aws/modules/worker/cloud-config.tf index d433294..3673b68 100644 --- a/aws/modules/worker/cloud-config.tf +++ b/aws/modules/worker/cloud-config.tf @@ -1,3 +1,19 @@ +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s-worker" { + input = "${ var.k8s-worker }" +} + +resource "gzip_me" "k8s-worker-key" { + input = "${ var.k8s-worker-key }" +} + data "template_file" "cloud-config" { template = "${ file( "${ path.module }/cloud-config.yml" )}" @@ -8,7 +24,8 @@ data "template_file" "cloud-config" { kubelet_version = "${ var.kubelet_version }" internal_tld = "${ var.internal_tld }" region = "${ var.region }" - ssl_tar = "/ssl/k8s-worker.tar.bz2" - # bucket = "${ var.s3_bucket }" + ca = "${ gzip_me.ca.output }" + k8s-worker = "${ gzip_me.k8s-worker.output }" + k8s-worker-key = "${ gzip_me.k8s-worker-key.output }" } } diff --git a/aws/modules/worker/cloud-config.yml b/aws/modules/worker/cloud-config.yml index e6aba53..9e26768 100644 --- a/aws/modules/worker/cloud-config.yml +++ b/aws/modules/worker/cloud-config.yml @@ -183,3 +183,21 @@ write-files: delaycompress copytruncate } + + - path: /etc/kubernetes/ssl/ca.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ ca } + + - path: /etc/kubernetes/ssl/k8s-worker.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-worker } + + - path: /etc/kubernetes/ssl/k8s-worker-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-worker-key } diff --git a/aws/modules/worker/ec2.tf b/aws/modules/worker/ec2.tf index 3b995d0..eb09b88 100644 --- a/aws/modules/worker/ec2.tf +++ b/aws/modules/worker/ec2.tf @@ -4,7 +4,7 @@ resource "aws_launch_configuration" "worker" { volume_size = "${ var.volume_size["ebs"] }" volume_type = "gp2" } - # iam_instance_profile = "${ var.instance-profile-name }" + iam_instance_profile = "${ var.instance-profile-name }" image_id = "${ var.ami-id }" instance_type = "${ var.instance-type }" key_name = "${ var.key-name }" diff --git a/aws/modules/worker/input.tf b/aws/modules/worker/input.tf index 39ffa52..441a44a 100644 --- a/aws/modules/worker/input.tf +++ b/aws/modules/worker/input.tf @@ -26,5 +26,8 @@ variable "volume_size" { } variable "vpc-id" {} variable "worker-name" {} -# variable "instance-profile-name" {} +variable "ca" {} +variable "k8s-worker" {} +variable "k8s-worker-key" {} +variable "instance-profile-name" {} # variable "s3_bucket" {} From 91aa5dfe215753ee75872158b265f82fa163aee2 Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 18:39:12 +1200 Subject: [PATCH 019/149] gzip encode kube-apiserver manifest to get under 16K cloud-init limit --- aws/modules/etcd/cloud-config.tf | 29 ++++++++++++-- aws/modules/etcd/cloud-config.yml | 62 ++--------------------------- aws/modules/etcd/ec2.tf | 2 +- aws/modules/etcd/input.tf | 2 +- aws/modules/etcd/kube-apiserver.yml | 58 +++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 65 deletions(-) create mode 100644 aws/modules/etcd/kube-apiserver.yml diff --git a/aws/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf index 77e9cf9..66b2273 100644 --- a/aws/modules/etcd/cloud-config.tf +++ b/aws/modules/etcd/cloud-config.tf @@ -22,6 +22,22 @@ resource "gzip_me" "k8s-apiserver-key" { input = "${ var.k8s-apiserver-key }" } +data "template_file" "kube-apiserver" { + template = "${ file( "${ path.module }/kube-apiserver.yml" )}" + + vars { + internal_tld = "${ var.internal_tld }" + service-cluster-ip-range = "${ var.service-cluster-ip-range }" + hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" + kubelet_aci = "${ var.kubelet_aci }" + kubelet_version = "${ var.kubelet_version }" + } +} + +resource "gzip_me" "kube-apiserver" { + input = "${ data.template_file.kube-apiserver.rendered }" +} + data "template_file" "cloud-config" { count = "${ length( split(",", var.etcd_ips) ) }" template = "${ file( "${ path.module }/cloud-config.yml" )}" @@ -30,7 +46,6 @@ data "template_file" "cloud-config" { cluster_domain = "${ var.cluster_domain }" cluster-token = "etcd-cluster-${ var.name }" dns_service_ip = "${ var.dns_service_ip }" - etc-tar = "/manifests/etc.tar" fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" hostname = "etcd${ count.index + 1 }" hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" @@ -44,9 +59,15 @@ data "template_file" "cloud-config" { k8s-etcd = "${ gzip_me.k8s-etcd.output }" k8s-etcd-key = "${ gzip_me.k8s-etcd-key.output }" k8s-apiserver = "${ gzip_me.k8s-apiserver.output }" - k8s-apiserver-key = "${ gzip_me.k8s-apiserver.output }" - ssl_tar = "ssl/k8s-apiserver.tar.bz2" - # bucket = "${var.s3_bucket}" + k8s-apiserver-key = "${ gzip_me.k8s-apiserver-key.output }" + kube-apiserver-yml = "${ gzip_me.kube-apiserver.output }" } } + + +# data "template_file" "kube-controller-manager" + +# data "template_file" "kube-proxy" + +# data "template_file" "kube-scheduler" diff --git a/aws/modules/etcd/cloud-config.yml b/aws/modules/etcd/cloud-config.yml index 70d4b1f..682b490 100644 --- a/aws/modules/etcd/cloud-config.yml +++ b/aws/modules/etcd/cloud-config.yml @@ -99,66 +99,10 @@ write-files: exec nsenter -m -u -i -n -p -t 1 -- /usr/bin/rkt "$@" - path: /etc/kubernetes/manifests/kube-apiserver.yml + encoding: "gzip+base64" content: | - apiVersion: v1 - kind: Pod - metadata: - name: kube-apiserver - namespace: kube-system - spec: - hostNetwork: true - containers: - - name: kube-apiserver - image: ${ hyperkube } - command: - - /hyperkube - - apiserver - - --admission-control=LimitRanger - - --admission-control=NamespaceExists - - --admission-control=NamespaceLifecycle - - --admission-control=ResourceQuota - - --admission-control=SecurityContextDeny - - --admission-control=ServiceAccount - - --allow-privileged=true - - --client-ca-file=/etc/kubernetes/ssl/ca.pem - - --cloud-provider=aws - - --etcd-servers=http://etcd.${ internal_tld }:2379 - - --insecure-bind-address=0.0.0.0 - - --secure-port=443 - - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --service-cluster-ip-range=${ service-cluster-ip-range } - - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem - - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --v=2 - livenessProbe: - httpGet: - host: 127.0.0.1 - port: 8080 - path: /healthz - initialDelaySeconds: 15 - timeoutSeconds: 15 - ports: - - containerPort: 443 - hostPort: 443 - name: https - - containerPort: 8080 - hostPort: 8080 - name: local - volumeMounts: - - mountPath: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - readOnly: true - - mountPath: /etc/ssl/certs - name: ssl-certs-host - readOnly: true - volumes: - - hostPath: - path: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - - hostPath: - path: /usr/share/ca-certificates - name: ssl-certs-host - + ${ kube-apiserver-yml } + - path: /etc/kubernetes/manifests/kube-controller-manager.yml content: | apiVersion: v1 diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 57407d3..50335bd 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -3,7 +3,7 @@ resource "aws_instance" "etcd" { ami = "${ var.ami-id }" associate_public_ip_address = false - # iam_instance_profile = "${ var.instance-profile-name }" + iam_instance_profile = "${ var.instance-profile-name }" instance_type = "${ var.instance-type }" key_name = "${ var.key-name }" private_ip = "${ element(split(",", var.etcd_ips), count.index) }" diff --git a/aws/modules/etcd/input.tf b/aws/modules/etcd/input.tf index 32e3bde..9f96d57 100644 --- a/aws/modules/etcd/input.tf +++ b/aws/modules/etcd/input.tf @@ -23,4 +23,4 @@ variable "k8s-etcd-key" {} variable "k8s-apiserver" {} variable "k8s-apiserver-key" {} # variable "s3_bucket" {} -# variable "instance-profile-name" {} +variable "instance-profile-name" {} diff --git a/aws/modules/etcd/kube-apiserver.yml b/aws/modules/etcd/kube-apiserver.yml new file mode 100644 index 0000000..331d58f --- /dev/null +++ b/aws/modules/etcd/kube-apiserver.yml @@ -0,0 +1,58 @@ +apiVersion: v1 +kind: Pod +metadata: + name: kube-apiserver + namespace: kube-system +spec: + hostNetwork: true + containers: + - name: kube-apiserver + image: ${ hyperkube } + command: + - /hyperkube + - apiserver + - --admission-control=LimitRanger + - --admission-control=NamespaceExists + - --admission-control=NamespaceLifecycle + - --admission-control=ResourceQuota + - --admission-control=SecurityContextDeny + - --admission-control=ServiceAccount + - --allow-privileged=true + - --client-ca-file=/etc/kubernetes/ssl/ca.pem + - --cloud-provider=aws + - --etcd-servers=http://etcd.${ internal_tld }:2379 + - --insecure-bind-address=0.0.0.0 + - --secure-port=443 + - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --service-cluster-ip-range=${ service-cluster-ip-range } + - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem + - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --v=2 + livenessProbe: + httpGet: + host: 127.0.0.1 + port: 8080 + path: /healthz + initialDelaySeconds: 15 + timeoutSeconds: 15 + ports: + - containerPort: 443 + hostPort: 443 + name: https + - containerPort: 8080 + hostPort: 8080 + name: local + volumeMounts: + - mountPath: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + readOnly: true + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host From afeda5668db994a972fe22f0fad66c63e19da26f Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 18:40:15 +1200 Subject: [PATCH 020/149] Remove S3 Module using cloud-init for Certs --- aws/modules/s3/input.tf | 5 ---- aws/modules/s3/output.tf | 2 -- aws/modules/s3/s3-cp | 56 ---------------------------------------- aws/modules/s3/s3.tf | 26 ------------------- 4 files changed, 89 deletions(-) delete mode 100644 aws/modules/s3/input.tf delete mode 100644 aws/modules/s3/output.tf delete mode 100755 aws/modules/s3/s3-cp delete mode 100644 aws/modules/s3/s3.tf diff --git a/aws/modules/s3/input.tf b/aws/modules/s3/input.tf deleted file mode 100644 index ef527a0..0000000 --- a/aws/modules/s3/input.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "name" {} -variable "region" {} -variable "s3_bucket" {} -variable "depends-id" {} -variable "data_dir" {} diff --git a/aws/modules/s3/output.tf b/aws/modules/s3/output.tf deleted file mode 100644 index 7748c2f..0000000 --- a/aws/modules/s3/output.tf +++ /dev/null @@ -1,2 +0,0 @@ -output "bucket" { value = "${ aws_s3_bucket.ssl.bucket }" } -output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } diff --git a/aws/modules/s3/s3-cp b/aws/modules/s3/s3-cp deleted file mode 100755 index eb3ccd4..0000000 --- a/aws/modules/s3/s3-cp +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -e - -function usage { cat < Date: Mon, 17 Apr 2017 05:03:36 +1200 Subject: [PATCH 021/149] _ replacement --- azure/cloud-config.tf | 4 ++-- azure/input.tf | 2 +- azure/keypair.tf | 6 +++--- azure/modules.tf | 14 +++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/azure/cloud-config.tf b/azure/cloud-config.tf index 1946ffd..0a040f7 100644 --- a/azure/cloud-config.tf +++ b/azure/cloud-config.tf @@ -3,7 +3,7 @@ resource "null_resource" "cloud_gen" { provisioner "local-exec" { command = < ${ var.data-dir }/azure-config.json +cat < ${ var.data_dir }/azure-config.json { "aadClientId": "$${ARM_CLIENT_ID}", "aadClientSecret": "$${ARM_CLIENT_SECRET}", @@ -25,7 +25,7 @@ COMMAND when = "destroy" on_failure = "continue" command = < Date: Mon, 17 Apr 2017 05:26:36 +1200 Subject: [PATCH 022/149] Simplify Azure vars --- .../bastion/{node.tf => bastion-node.tf} | 6 +- .../{user-data.yml => bastion-user-data.yml} | 0 azure/modules/etcd/cloud-config.tf | 24 ---- ...cloud-config.yml => etcd-cloud-config.yml} | 0 ...load-balancer.tf => etcd-load-balancer.tf} | 0 .../modules/etcd/{nodes.tf => etcd-nodes.tf} | 25 ++++ azure/modules/etcd/output.tf | 5 - azure/modules/security/io.tf | 9 -- azure/modules/security/security.tf | 123 ------------------ azure/modules/vpc/azure-security.tf | 6 - azure/modules/vpc/azure-subnet.tf | 61 --------- azure/modules/vpc/input.tf | 4 + azure/modules/vpc/io.tf | 16 --- azure/modules/vpc/output.tf | 1 + azure/modules/vpc/private.tf | 54 -------- azure/modules/vpc/public.tf | 44 ------- .../vpc/{vpc.tf => virtual_network.tf} | 26 ++-- azure/modules/worker/cloud-config.tf | 14 -- ...oud-config.yml => worker-cloud-config.yml} | 0 .../worker/{nodes.tf => worker-nodes.tf} | 15 +++ 20 files changed, 59 insertions(+), 374 deletions(-) rename azure/modules/bastion/{node.tf => bastion-node.tf} (91%) rename azure/modules/bastion/{user-data.yml => bastion-user-data.yml} (100%) delete mode 100644 azure/modules/etcd/cloud-config.tf rename azure/modules/etcd/{cloud-config.yml => etcd-cloud-config.yml} (100%) rename azure/modules/etcd/{load-balancer.tf => etcd-load-balancer.tf} (100%) rename azure/modules/etcd/{nodes.tf => etcd-nodes.tf} (67%) delete mode 100644 azure/modules/security/io.tf delete mode 100644 azure/modules/security/security.tf delete mode 100644 azure/modules/vpc/azure-security.tf delete mode 100644 azure/modules/vpc/azure-subnet.tf create mode 100644 azure/modules/vpc/input.tf delete mode 100644 azure/modules/vpc/io.tf create mode 100644 azure/modules/vpc/output.tf delete mode 100644 azure/modules/vpc/private.tf delete mode 100644 azure/modules/vpc/public.tf rename azure/modules/vpc/{vpc.tf => virtual_network.tf} (73%) delete mode 100644 azure/modules/worker/cloud-config.tf rename azure/modules/worker/{cloud-config.yml => worker-cloud-config.yml} (100%) rename azure/modules/worker/{nodes.tf => worker-nodes.tf} (85%) diff --git a/azure/modules/bastion/node.tf b/azure/modules/bastion/bastion-node.tf similarity index 91% rename from azure/modules/bastion/node.tf rename to azure/modules/bastion/bastion-node.tf index 44e8adb..4cc5d74 100644 --- a/azure/modules/bastion/node.tf +++ b/azure/modules/bastion/bastion-node.tf @@ -45,7 +45,7 @@ resource "azurerm_virtual_machine" "cncf" { computer_name = "hostname" admin_username = "${ var.admin_username }" admin_password = "Password1234!" - custom_data = "${ data.template_file.user-data.rendered }" + custom_data = "${ data.template_file.bastion-user-data.rendered }" #custom_data = "${file("${path.module}/user-data2.yml")}" } @@ -58,8 +58,8 @@ resource "azurerm_virtual_machine" "cncf" { } } -data "template_file" "user-data" { - template = "${ file( "${ path.module }/user-data.yml" )}" +data "template_file" "bastion-user-data" { + template = "${ file( "${ path.module }/bastion-user-data.yml" )}" vars { internal_tld = "${ var.internal_tld }" } diff --git a/azure/modules/bastion/user-data.yml b/azure/modules/bastion/bastion-user-data.yml similarity index 100% rename from azure/modules/bastion/user-data.yml rename to azure/modules/bastion/bastion-user-data.yml diff --git a/azure/modules/etcd/cloud-config.tf b/azure/modules/etcd/cloud-config.tf deleted file mode 100644 index 158d20c..0000000 --- a/azure/modules/etcd/cloud-config.tf +++ /dev/null @@ -1,24 +0,0 @@ -data "template_file" "cloud-config" { - count = "${ var.master_node_count }" - template = "${ file( "${ path.module }/cloud-config.yml" )}" - - vars { - # bucket = "${ var.s3_bucket }" - cluster_domain = "${ var.cluster_domain }" - cluster-token = "etcd-cluster-${ var.name }" - dns_service_ip = "${ var.dns_service_ip }" - etc-tar = "/manifests/etc.tar" - fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" - hostname = "etcd${ count.index + 1 }" - kubelet_image_url = "${ var.kubelet_image_url }" - kubelet_image_tag = "${ var.kubelet_image_tag }" - internal_tld = "${ var.internal_tld }" - pod_cidr = "${ var.pod_cidr }" - location = "${ var.location }" - service_cidr = "${ var.service_cidr }" - k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" - node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" - cloud-config = "${ base64encode(var.cloud-config) }" - - } -} diff --git a/azure/modules/etcd/cloud-config.yml b/azure/modules/etcd/etcd-cloud-config.yml similarity index 100% rename from azure/modules/etcd/cloud-config.yml rename to azure/modules/etcd/etcd-cloud-config.yml diff --git a/azure/modules/etcd/load-balancer.tf b/azure/modules/etcd/etcd-load-balancer.tf similarity index 100% rename from azure/modules/etcd/load-balancer.tf rename to azure/modules/etcd/etcd-load-balancer.tf diff --git a/azure/modules/etcd/nodes.tf b/azure/modules/etcd/etcd-nodes.tf similarity index 67% rename from azure/modules/etcd/nodes.tf rename to azure/modules/etcd/etcd-nodes.tf index 8001df9..46746f7 100644 --- a/azure/modules/etcd/nodes.tf +++ b/azure/modules/etcd/etcd-nodes.tf @@ -51,3 +51,28 @@ resource "azurerm_virtual_machine" "cncf" { } } } + +data "template_file" "etcd-cloud-config" { + count = "${ var.master_node_count }" + template = "${ file( "${ path.module }/etcd-cloud-config.yml" )}" + + vars { + # bucket = "${ var.s3_bucket }" + cluster_domain = "${ var.cluster_domain }" + cluster-token = "etcd-cluster-${ var.name }" + dns_service_ip = "${ var.dns_service_ip }" + etc-tar = "/manifests/etc.tar" + fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" + hostname = "etcd${ count.index + 1 }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + internal_tld = "${ var.internal_tld }" + pod_cidr = "${ var.pod_cidr }" + location = "${ var.location }" + service_cidr = "${ var.service_cidr }" + k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" + node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" + cloud-config = "${ base64encode(var.cloud-config) }" + + } +} diff --git a/azure/modules/etcd/output.tf b/azure/modules/etcd/output.tf index 1e7929a..955bae8 100644 --- a/azure/modules/etcd/output.tf +++ b/azure/modules/etcd/output.tf @@ -1,8 +1,3 @@ output "external-lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } - - -# output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } -# output "external-elb" { value = "${ aws_elb.external.dns_name }" } -# output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } output "master-ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } diff --git a/azure/modules/security/io.tf b/azure/modules/security/io.tf deleted file mode 100644 index 6c643c0..0000000 --- a/azure/modules/security/io.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "allow_ssh_cidr" {} -variable "vpc_cidr" {} -variable "name" {} -variable "vpc-id" {} - -output "bastion-id" { value = "${ aws_security_group.bastion.id }" } -output "etcd-id" { value = "${ aws_security_group.etcd.id }" } -output "external-elb-id" { value = "${ aws_security_group.external-elb.id }" } -output "worker-id" { value = "${ aws_security_group.worker.id }" } diff --git a/azure/modules/security/security.tf b/azure/modules/security/security.tf deleted file mode 100644 index 2c82e7d..0000000 --- a/azure/modules/security/security.tf +++ /dev/null @@ -1,123 +0,0 @@ -resource "aws_security_group" "bastion" { - description = "k8s bastion security group" - - egress = { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress = { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = [ "${ var.allow_ssh_cidr }" ] - } - - name = "bastion-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "bastion-k8s-${ var.name }" - builtWith = "terraform" - } - - vpc_id = "${ var.vpc-id }" -} - -resource "aws_security_group" "etcd" { - description = "k8s etcd security group" - - egress = { - from_port = 0 - to_port = 0 - protocol = "-1" - /*self = true*/ - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress = { - from_port = 0 - to_port = 0 - protocol = "-1" - self = true - cidr_blocks = [ "${ var.vpc_cidr }" ] - } - - name = "etcd-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "etcd-k8s-${ var.name }" - builtWith = "terraform" - } - - vpc_id = "${ var.vpc-id }" -} - -resource "aws_security_group" "external-elb" { - description = "k8s-${ var.name } master (apiserver) external elb" - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - /*cidr_blocks = [ "${ var.vpc_cidr }" ]*/ - security_groups = [ "${ aws_security_group.etcd.id }" ] - } - - ingress { - from_port = -1 - to_port = -1 - protocol = "icmp" - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress { - from_port = 443 - to_port = 443 - protocol = "tcp" - cidr_blocks = [ "0.0.0.0/0" ] - } - - name = "master-external-elb-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "master-external-elb-k8s-${ var.name }" - builtWith = "terraform" - } - - vpc_id = "${ var.vpc-id }" -} - -resource "aws_security_group" "worker" { - description = "k8s worker security group" - - egress = { - from_port = 0 - to_port = 0 - protocol = "-1" - /*self = true*/ - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress = { - from_port = 0 - to_port = 0 - protocol = "-1" - self = true - cidr_blocks = [ "${ var.vpc_cidr }" ] - } - - name = "worker-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "worker-k8s-${ var.name }" - builtWith = "terraform" - } - - vpc_id = "${ var.vpc-id }" -} diff --git a/azure/modules/vpc/azure-security.tf b/azure/modules/vpc/azure-security.tf deleted file mode 100644 index b098f09..0000000 --- a/azure/modules/vpc/azure-security.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "azurerm_network_security_group" "cncf" { - name = "${ var.name }" - location = "${ var.location }" - resource_group_name = "${ var.name }" - - } diff --git a/azure/modules/vpc/azure-subnet.tf b/azure/modules/vpc/azure-subnet.tf deleted file mode 100644 index c48fda6..0000000 --- a/azure/modules/vpc/azure-subnet.tf +++ /dev/null @@ -1,61 +0,0 @@ -resource "azurerm_subnet" "cncf" { - name = "${ var.name }" - resource_group_name = "${ var.name }" - virtual_network_name = "${azurerm_virtual_network.cncf.name}" - address_prefix = "10.0.10.0/24" - route_table_id = "${ azurerm_route_table.cncf.id }" - -} - -/* -# gateway" "nat" { - depends_on = [ - "aws_eip.nat", - "aws_internet_gateway.main", - ] - - allocation_id = "${ aws_eip.nat.id }" - subnet_id = "${ aws_subnet.public.0.id }" -} - -resource "aws_subnet" "private" { - count = "${ length( split(",", var.azs) ) }" - - availability_zone = "${ element( split(",", var.azs), count.index ) }" - cidr_block = "${ cidrsubnet(var.cidr, 8, count.index + 10) }" - vpc_id = "${ aws_vpc.cncf.id }" - - tags { - "kubernetes.io/role/internal-elb" = "${ var.name }" - builtWith = "terraform" - KubernetesCluster = "${ var.name }" - kz8s = "${ var.name }" - Name = "kz8s-${ var.name }-private" - visibility = "private" - } -} - -resource "aws_route_table" "private" { - vpc_id = "${ aws_vpc.cncf.id }" - - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = "${ aws_nat_gateway.nat.id }" - } - - tags { - builtWith = "terraform" - KubernetesCluster = "${ var.name }" - kz8s = "${ var.name }" - Name = "kz8s-${ var.name }" - visibility = "private" - } -} - -resource "aws_route_table_association" "private" { - count = "${ length(split(",", var.azs)) }" - - route_table_id = "${ aws_route_table.private.id }" - subnet_id = "${ element(aws_subnet.private.*.id, count.index) }" -} -*/ diff --git a/azure/modules/vpc/input.tf b/azure/modules/vpc/input.tf new file mode 100644 index 0000000..7e1a54e --- /dev/null +++ b/azure/modules/vpc/input.tf @@ -0,0 +1,4 @@ +variable "cidr" {} +variable "name" {} +variable "name-servers-file" {} +variable "location" {} diff --git a/azure/modules/vpc/io.tf b/azure/modules/vpc/io.tf deleted file mode 100644 index 8597b4f..0000000 --- a/azure/modules/vpc/io.tf +++ /dev/null @@ -1,16 +0,0 @@ -#variable "azs" {} -variable "cidr" {} -#variable "kubelet_version" {} -#variable "depends-id" {} -variable "name" {} -variable "name-servers-file" {} -variable "location" {} -#variable "region" {} - -#output "depends-id" { value = "${null_resource.dummy_dependency.id}" } -#output "gateway-id" { value = "${ aws_internet_gateway.cncf.id }" } -#output "id" { value = "${ aws_vpc.cncf.id }" } -#output "route-table-id" { value = "${ aws_route_table.private.id }" } -#output "subnet-ids-private" { value = "${ join(",", aws_subnet.private.*.id) }" } -#output "subnet-ids-public" { value = "${ join(",", aws_subnet.public.*.id) }" } -output "subnet-id" { value = "${ azurerm_subnet.cncf.id }" } diff --git a/azure/modules/vpc/output.tf b/azure/modules/vpc/output.tf new file mode 100644 index 0000000..7c09587 --- /dev/null +++ b/azure/modules/vpc/output.tf @@ -0,0 +1 @@ +output "subnet-id" { value = "${ azurerm_subnet.cncf.id }" } diff --git a/azure/modules/vpc/private.tf b/azure/modules/vpc/private.tf deleted file mode 100644 index dc6fcdc..0000000 --- a/azure/modules/vpc/private.tf +++ /dev/null @@ -1,54 +0,0 @@ -/* -resource "aws_eip" "nat" { vpc = true } - -resource "aws_nat_gateway" "nat" { - depends_on = [ - "aws_eip.nat", - "aws_internet_gateway.main", - ] - - allocation_id = "${ aws_eip.nat.id }" - subnet_id = "${ aws_subnet.public.0.id }" -} - -resource "aws_subnet" "private" { - count = "${ length( split(",", var.azs) ) }" - - availability_zone = "${ element( split(",", var.azs), count.index ) }" - cidr_block = "${ cidrsubnet(var.cidr, 8, count.index + 10) }" - vpc_id = "${ aws_vpc.cncf.id }" - - tags { - "kubernetes.io/role/internal-elb" = "${ var.name }" - builtWith = "terraform" - KubernetesCluster = "${ var.name }" - kz8s = "${ var.name }" - Name = "kz8s-${ var.name }-private" - visibility = "private" - } -} - -resource "aws_route_table" "private" { - vpc_id = "${ aws_vpc.cncf.id }" - - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = "${ aws_nat_gateway.nat.id }" - } - - tags { - builtWith = "terraform" - KubernetesCluster = "${ var.name }" - kz8s = "${ var.name }" - Name = "kz8s-${ var.name }" - visibility = "private" - } -} - -resource "aws_route_table_association" "private" { - count = "${ length(split(",", var.azs)) }" - - route_table_id = "${ aws_route_table.private.id }" - subnet_id = "${ element(aws_subnet.private.*.id, count.index) }" -} -*/ diff --git a/azure/modules/vpc/public.tf b/azure/modules/vpc/public.tf deleted file mode 100644 index ceff640..0000000 --- a/azure/modules/vpc/public.tf +++ /dev/null @@ -1,44 +0,0 @@ -/* -resource "aws_internet_gateway" "main" { - vpc_id = "${ aws_vpc.cncf.id }" - - tags { - builtWith = "terraform" - KubernetesCluster = "${ var.name }" - kz8s = "${ var.name }" - Name = "kz8s-${ var.name }" - version = "${ var.kubelet_version }" - } -} - -resource "aws_subnet" "public" { - count = "${ length( split(",", var.azs) ) }" - - availability_zone = "${ element( split(",", var.azs), count.index ) }" - cidr_block = "${ cidrsubnet(var.cidr, 8, count.index) }" - vpc_id = "${ aws_vpc.cncf.id }" - - tags { - "kubernetes.io/role/elb" = "${ var.name }" - builtWith = "terraform" - KubernetesCluster = "${ var.name }" - kz8s = "${ var.name }" - Name = "kz8s-${ var.name }-public" - version = "${ var.kubelet_version }" - visibility = "public" - } -} - -resource "aws_route" "public" { - route_table_id = "${ aws_vpc.cncf.main_route_table_id }" - destination_cidr_block = "0.0.0.0/0" - gateway_id = "${ aws_internet_gateway.cncf.id }" -} - -resource "aws_route_table_association" "public" { - count = "${ length(split(",", var.azs)) }" - - route_table_id = "${ aws_vpc.cncf.main_route_table_id }" - subnet_id = "${ element(aws_subnet.public.*.id, count.index) }" -} -*/ diff --git a/azure/modules/vpc/vpc.tf b/azure/modules/vpc/virtual_network.tf similarity index 73% rename from azure/modules/vpc/vpc.tf rename to azure/modules/vpc/virtual_network.tf index 136f172..c03c9b1 100644 --- a/azure/modules/vpc/vpc.tf +++ b/azure/modules/vpc/virtual_network.tf @@ -1,10 +1,15 @@ -#resource "null_resource" "dummy_dependency" { -# depends_on = [ -# "aws_vpc.main", -# "aws_nat_gateway.nat" -# ] -#} +resource "azurerm_network_security_group" "cncf" { + name = "${ var.name }" +} + +resource "azurerm_subnet" "cncf" { + name = "${ var.name }" + resource_group_name = "${ var.name }" + virtual_network_name = "${azurerm_virtual_network.cncf.name}" + address_prefix = "10.0.10.0/24" + route_table_id = "${ azurerm_route_table.cncf.id }" +} resource "azurerm_virtual_network" "cncf" { name = "${ var.name }" @@ -25,15 +30,6 @@ resource "azurerm_virtual_network" "cncf" { # dns_servers.1: "" => "13.107.24.9" # dns_servers.2: "" => "64.4.48.9" # dns_servers.3: "" => "13.107.160.9" - - # tags { - #builtWith = "terraform" - #KubernetesCluster = "${ var.name }" - #z8s = "${ var.name }" - #Name = "kz8s-${ var.name }" - #version = "${ var.kubelet_version }" - #visibility = "private,public" - #} } resource "azurerm_route_table" "cncf" { diff --git a/azure/modules/worker/cloud-config.tf b/azure/modules/worker/cloud-config.tf deleted file mode 100644 index e5897ab..0000000 --- a/azure/modules/worker/cloud-config.tf +++ /dev/null @@ -1,14 +0,0 @@ -data "template_file" "cloud-config" { - template = "${ file( "${ path.module }/cloud-config.yml" )}" - - vars { - cluster_domain = "${ var.cluster_domain }" - dns_service_ip = "${ var.dns_service_ip }" - kubelet_image_url = "${ var.kubelet_image_url }" - kubelet_image_tag = "${ var.kubelet_image_tag }" - internal_tld = "${ var.internal_tld }" - location = "${ var.location }" - k8s-worker-tar = "${ base64encode(var.k8s-worker-tar) }" - cloud-config = "${ base64encode(var.cloud-config) }" - } -} diff --git a/azure/modules/worker/cloud-config.yml b/azure/modules/worker/worker-cloud-config.yml similarity index 100% rename from azure/modules/worker/cloud-config.yml rename to azure/modules/worker/worker-cloud-config.yml diff --git a/azure/modules/worker/nodes.tf b/azure/modules/worker/worker-nodes.tf similarity index 85% rename from azure/modules/worker/nodes.tf rename to azure/modules/worker/worker-nodes.tf index 9cd414d..abddccf 100644 --- a/azure/modules/worker/nodes.tf +++ b/azure/modules/worker/worker-nodes.tf @@ -1,3 +1,18 @@ +data "template_file" "worker-cloud-config" { + template = "${ file( "${ path.module }/worker-cloud-config.yml" )}" + + vars { + cluster_domain = "${ var.cluster_domain }" + dns_service_ip = "${ var.dns_service_ip }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + internal_tld = "${ var.internal_tld }" + location = "${ var.location }" + k8s-worker-tar = "${ base64encode(var.k8s-worker-tar) }" + cloud-config = "${ base64encode(var.cloud-config) }" + } +} + resource "azurerm_network_interface" "cncf" { count = "${ var.worker_node_count }" name = "worker-interface${ count.index + 1 }" From b1aa72ed4759fbbcb72b94f31a74bfc708bd1d71 Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 17 Apr 2017 06:18:38 +1200 Subject: [PATCH 023/149] Refactor AWS to Support Muilt Deploys on the same AWS Account --- aws/input.tf | 6 +++--- aws/modules.tf | 7 ++++--- aws/modules/etcd/ec2.tf | 2 +- aws/modules/kubeconfig/io.tf | 1 + aws/modules/kubeconfig/kubeconfig.tf | 2 +- aws/modules/worker/ec2.tf | 2 +- aws/output.tf | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/aws/input.tf b/aws/input.tf index 737f4c3..c8b5996 100644 --- a/aws/input.tf +++ b/aws/input.tf @@ -1,6 +1,6 @@ -variable "name" {} +variable "name" { default = "aws" } variable "aws_region" { default = "ap-southeast-2" } -variable "aws_key_name" { default = "ii-cncfdemo" } +variable "aws_key_name" { default = "aws" } variable "aws_azs" { default = "ap-southeast-2a,ap-southeast-2b,ap-southeast-2c" } variable "internal_tld" { default = "aws.cncf.demo" } @@ -23,4 +23,4 @@ variable "aws_bastion_vm_size" { default = "t2.nano" } variable "kubelet_aci" { default = "quay.io/coreos/hyperkube"} variable "kubelet_version" { default = "v1.5.1_coreos.0"} -variable "data_dir" { default = "/cncf/data" } +variable "data_dir" { default = "/cncf/data/aws" } diff --git a/aws/modules.tf b/aws/modules.tf index fcf0ba1..3fdaa23 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -117,9 +117,10 @@ module "worker" { module "kubeconfig" { source = "./modules/kubeconfig" - admin-key-pem = "${ var.data_dir }/k8s-admin-key.pem" - admin-pem = "${ var.data_dir }/k8s-admin.pem" - ca-pem = "${ var.data_dir }/ca.pem" + admin-key-pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" + admin-pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" + ca-pem = "${ var.data_dir }/.cfssl/ca.pem" + data_dir = "${ var.data_dir }" master-elb = "${ module.etcd.external-elb }" name = "${ var.name }" } diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 50335bd..2a6536c 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -21,7 +21,7 @@ resource "aws_instance" "etcd" { depends-id = "${ var.depends-id }" KubernetesCluster = "${ var.name }" # used by kubelet's aws provider to determine cluster kz8s = "${ var.name }" - Name = "kz8s-etcd${ count.index + 1 }" + Name = "etcd${ count.index + 1 }-${ var.name }" role = "etcd,apiserver" version = "${ var.kubelet_version }" visibility = "private" diff --git a/aws/modules/kubeconfig/io.tf b/aws/modules/kubeconfig/io.tf index 2075fc6..b89a245 100644 --- a/aws/modules/kubeconfig/io.tf +++ b/aws/modules/kubeconfig/io.tf @@ -3,5 +3,6 @@ variable "admin-pem" {} variable "ca-pem" {} variable "master-elb" {} variable "name" {} +variable "data_dir" {} output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/aws/modules/kubeconfig/kubeconfig.tf b/aws/modules/kubeconfig/kubeconfig.tf index 6fe9b8c..eeae7af 100644 --- a/aws/modules/kubeconfig/kubeconfig.tf +++ b/aws/modules/kubeconfig/kubeconfig.tf @@ -25,7 +25,7 @@ resource "null_resource" "kubeconfig" { provisioner "local-exec" { command = < ./tmp/kubeconfig + cat <<'__USERDATA__' > ${ var.data_dir }/kubeconfig ${data.template_file.kubeconfig.rendered} __USERDATA__ LOCAL_EXEC diff --git a/aws/modules/worker/ec2.tf b/aws/modules/worker/ec2.tf index eb09b88..9a8523d 100644 --- a/aws/modules/worker/ec2.tf +++ b/aws/modules/worker/ec2.tf @@ -65,7 +65,7 @@ resource "aws_autoscaling_group" "worker" { tag { key = "Name" - value = "kz8s-worker" + value = "worker-${ var.name }" propagate_at_launch = true } diff --git a/aws/output.tf b/aws/output.tf index 664cae5..7664793 100644 --- a/aws/output.tf +++ b/aws/output.tf @@ -11,6 +11,6 @@ output "region" { value = "${ var.aws_region }" } output "subnet-ids-private" { value = "${ module.vpc.subnet-ids-private }" } output "subnet-ids-public" { value = "${ module.vpc.subnet-ids-public }" } output "worker-autoscaling-group-name" { value = "${ module.worker.autoscaling-group-name }" } -output "ssh-key-setup" { value = "eval $(ssh-agent) ; ssh-add data/ii-cncfdemo.pem"} -output "ssh-via-bastion" { value = "ssh -At ${ var.admin_username }@${ module.bastion.ip } ssh ${ var.admin_username }@master1.cncf.demo"} +output "ssh-key-setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir}/${ var.name}.pem" } +output "ssh-via-bastion" { value = "ssh -At ${ var.admin_username }@${ module.bastion.ip } ssh ${ var.admin_username }@etcd1.${ var.internal_tld }"} From 690c361b13a345a069dcd8bb78f32e57f0f4db18 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 06:25:12 +1200 Subject: [PATCH 024/149] Refactor certs / yaml config to use gzip+base64 --- azure/modules.tf | 6 +- azure/modules/etcd/etcd-cloud-config.yml | 113 ++++++------------- azure/modules/etcd/etcd-nodes.tf | 54 ++++++++- azure/modules/etcd/input.tf | 6 +- azure/modules/kubeconfig/io.tf | 8 -- azure/modules/worker/input.tf | 4 +- azure/modules/worker/worker-cloud-config.yml | 37 +++--- azure/modules/worker/worker-nodes.tf | 80 ++++--------- 8 files changed, 142 insertions(+), 166 deletions(-) delete mode 100644 azure/modules/kubeconfig/io.tf diff --git a/azure/modules.tf b/azure/modules.tf index b0ecd0f..6d9e6e9 100644 --- a/azure/modules.tf +++ b/azure/modules.tf @@ -36,8 +36,12 @@ module "dns" { internal_tld = "${ var.internal_tld }" pod_cidr = "${ var.pod_cidr }" service_cidr = "${ var.service_cidr }" - k8s-apiserver-tar = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.tar")}" cloud-config = "${file("${ var.data_dir }/azure-config.json")}" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s-etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" + k8s-etcd-key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" + k8s-apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" + k8s-apiserver-key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" # etcd-security-group-id = "${ module.security.etcd-id }" # external-elb-security-group-id = "${ module.security.external-elb-id }" } diff --git a/azure/modules/etcd/etcd-cloud-config.yml b/azure/modules/etcd/etcd-cloud-config.yml index f643127..b378272 100644 --- a/azure/modules/etcd/etcd-cloud-config.yml +++ b/azure/modules/etcd/etcd-cloud-config.yml @@ -23,12 +23,6 @@ coreos: units: - name: etcd2.service command: start - drop-ins: - - name: wait-for-certs.conf - content: | - [Unit] - After=get-ssl.service - Requires=get-ssl.service - name: docker.service command: start @@ -38,14 +32,6 @@ coreos: [Service] Environment="DOCKER_OPTS=--storage-driver=overlay" - - name: get-ssl.service - command: start - content: | - [Service] - ExecStart=/bin/sh -c "tar xvj -f /etc/kubernetes/ssl/k8s-apiserver.tar.bz2 -C /etc/kubernetes/ssl/" - RemainAfterExit=yes - Type=oneshot - - name: kubelet.service command: start content: | @@ -96,66 +82,6 @@ write-files: - path: /etc/kubernetes/manifests/kube-apiserver.yml content: | - apiVersion: v1 - kind: Pod - metadata: - name: kube-apiserver - namespace: kube-system - spec: - hostNetwork: true - containers: - - name: kube-apiserver - image: ${ kubelet_image_url }:${ kubelet_image_tag } - command: - - /hyperkube - - apiserver - - --admission-control=LimitRanger - - --admission-control=NamespaceExists - - --admission-control=NamespaceLifecycle - - --admission-control=ResourceQuota - - --admission-control=SecurityContextDeny - - --admission-control=ServiceAccount - - --allow-privileged=true - - --client-ca-file=/etc/kubernetes/ssl/ca.pem - - --cloud-provider=azure - - --cloud-config=/etc/kubernetes/ssl/azure-config.json - - --etcd-servers=http://etcd.${ internal_tld }:2379 - - --insecure-bind-address=0.0.0.0 - - --secure-port=443 - - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --service-cluster-ip-range=${ service_cidr } - - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem - - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --v=2 - livenessProbe: - httpGet: - host: 127.0.0.1 - port: 8080 - path: /healthz - initialDelaySeconds: 15 - timeoutSeconds: 15 - ports: - - containerPort: 443 - hostPort: 443 - name: https - - containerPort: 8080 - hostPort: 8080 - name: local - volumeMounts: - - mountPath: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - readOnly: true - - mountPath: /etc/ssl/certs - name: ssl-certs-host - readOnly: true - volumes: - - hostPath: - path: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - - hostPath: - path: /usr/share/ca-certificates - name: ssl-certs-host - - path: /etc/kubernetes/manifests/kube-controller-manager.yml content: | apiVersion: v1 @@ -270,11 +196,38 @@ write-files: copytruncate } - - encoding: b64 - content: ${ k8s-apiserver-tar } - path: /etc/kubernetes/ssl/k8s-apiserver.tar.bz2 + - path: /etc/kubernetes/ssl/ca.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ ca } + + - path: /etc/kubernetes/ssl/k8s-etcd.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-etcd } + + - path: /etc/kubernetes/ssl/k8s-etcd-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-etcd-key } + + - path: /etc/kubernetes/ssl/k8s-apiserver.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-apiserver } - - encoding: b64 - content: ${ cloud-config } - path: /etc/kubernetes/ssl/azure-config.json + - path: /etc/kubernetes/ssl/k8s-apiserver-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-apiserver-key } + - path: /etc/kubernetes/ssl/azure-config.json + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ cloud-config } diff --git a/azure/modules/etcd/etcd-nodes.tf b/azure/modules/etcd/etcd-nodes.tf index 46746f7..7896985 100644 --- a/azure/modules/etcd/etcd-nodes.tf +++ b/azure/modules/etcd/etcd-nodes.tf @@ -47,11 +47,53 @@ resource "azurerm_virtual_machine" "cncf" { disable_password_authentication = true ssh_keys { path = "/home/${ var.admin_username }/.ssh/authorized_keys" - key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" + key_data = "${file("${var.data_dir}.ssh/id_rsa.pub")}" } } } +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "kube-apiserver" { + input = "${ data.template_file.kube-apiserver.rendered }" +} +resource "gzip_me" "cloud-config" { + input = "${ var.cloud-config }" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s-etcd" { + input = "${ var.k8s-etcd }" +} + +resource "gzip_me" "k8s-etcd-key" { + input = "${ var.k8s-etcd-key }" +} + +resource "gzip_me" "k8s-apiserver" { + input = "${ var.k8s-apiserver }" +} + +resource "gzip_me" "k8s-apiserver-key" { + input = "${ var.k8s-apiserver-key }" +} + +data "template_file" "kube-apiserver" { + template = "${ file( "${ path.module }/kube-apiserver.yml" )}" + vars { + internal_tld = "${ var.internal_tld }" + service-cluster-ip-range = "${ var.service-cluster-ip-range }" + hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" + kubelet_aci = "${ var.kubelet_aci }" + kubelet_version = "${ var.kubelet_version }" + } +} + data "template_file" "etcd-cloud-config" { count = "${ var.master_node_count }" template = "${ file( "${ path.module }/etcd-cloud-config.yml" )}" @@ -61,7 +103,6 @@ data "template_file" "etcd-cloud-config" { cluster_domain = "${ var.cluster_domain }" cluster-token = "etcd-cluster-${ var.name }" dns_service_ip = "${ var.dns_service_ip }" - etc-tar = "/manifests/etc.tar" fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" hostname = "etcd${ count.index + 1 }" kubelet_image_url = "${ var.kubelet_image_url }" @@ -70,9 +111,14 @@ data "template_file" "etcd-cloud-config" { pod_cidr = "${ var.pod_cidr }" location = "${ var.location }" service_cidr = "${ var.service_cidr }" - k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" + cloud-config = "${ gzip_me.cloud-config }" + ca = "${ gzip_me.ca.output }" + k8s-etcd = "${ gzip_me.k8s-etcd.output }" + k8s-etcd-key = "${ gzip_me.k8s-etcd-key.output }" + k8s-apiserver = "${ gzip_me.k8s-apiserver.output }" + k8s-apiserver-key = "${ gzip_me.k8s-apiserver.output }" + k8s-apiserver-yml = "${ gzip_me.kube-apiserver.output }" node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" - cloud-config = "${ base64encode(var.cloud-config) }" } } diff --git a/azure/modules/etcd/input.tf b/azure/modules/etcd/input.tf index a7835cb..e82be8e 100644 --- a/azure/modules/etcd/input.tf +++ b/azure/modules/etcd/input.tf @@ -21,6 +21,10 @@ variable "admin_username" {} variable "kubelet_image_url" {} variable "kubelet_image_tag" {} variable "cloud-config" {} - # variable "etcd-security-group-id" {} # variable "external-elb-security-group-id" {} +variable "ca" {} +variable "k8s-etcd" {} +variable "k8s-etcd-key" {} +variable "k8s-apiserver" {} +variable "k8s-apiserver-key" {} diff --git a/azure/modules/kubeconfig/io.tf b/azure/modules/kubeconfig/io.tf deleted file mode 100644 index fda7916..0000000 --- a/azure/modules/kubeconfig/io.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "admin-key-pem" {} -variable "admin-pem" {} -variable "ca-pem" {} -variable "fqdn-k8s" {} -variable "name" {} - - -output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/azure/modules/worker/input.tf b/azure/modules/worker/input.tf index f38e89e..5fff248 100644 --- a/azure/modules/worker/input.tf +++ b/azure/modules/worker/input.tf @@ -16,7 +16,9 @@ variable "cluster_domain" {} variable "dns_service_ip" {} variable "internal_tld" {} variable "admin_username" {} -variable "k8s-worker-tar" {} variable "kubelet_image_url" {} variable "kubelet_image_tag" {} variable "cloud-config" {} +variable "ca" {} +variable "k8s-worker" {} +variable "k8s-worker-key" {} diff --git a/azure/modules/worker/worker-cloud-config.yml b/azure/modules/worker/worker-cloud-config.yml index 51b4755..700b3c6 100644 --- a/azure/modules/worker/worker-cloud-config.yml +++ b/azure/modules/worker/worker-cloud-config.yml @@ -1,5 +1,4 @@ #cloud-config - --- coreos: @@ -18,14 +17,6 @@ coreos: - name: docker.service command: start - - name: get-ssl.service - command: start - content: | - [Service] - ExecStart=/bin/sh -c "tar xvj -f /etc/kubernetes/ssl/k8s-worker.tar.bz2 -C /etc/kubernetes/ssl/" - RemainAfterExit=yes - Type=oneshot - - name: kubelet.service command: start content: | @@ -148,11 +139,27 @@ write-files: delaycompress copytruncate } + + - path: /etc/kubernetes/ssl/ca.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ ca } + + - path: /etc/kubernetes/ssl/k8s-worker.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-worker } - - encoding: b64 - content: ${ k8s-worker-tar } - path: /etc/kubernetes/ssl/k8s-worker.tar.bz2 + - path: /etc/kubernetes/ssl/k8s-worker-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s-worker-key } - - encoding: b64 - content: ${ cloud-config } - path: /etc/kubernetes/ssl/azure-config.json + - path: /etc/kubernetes/ssl/azure-config.json + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ cloud-config } diff --git a/azure/modules/worker/worker-nodes.tf b/azure/modules/worker/worker-nodes.tf index abddccf..d341411 100644 --- a/azure/modules/worker/worker-nodes.tf +++ b/azure/modules/worker/worker-nodes.tf @@ -1,3 +1,23 @@ +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "cloud-config" { + input = "${ var.cloud-config }" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s-worker" { + input = "${ var.k8s-worker }" +} + +resource "gzip_me" "k8s-worker-key" { + input = "${ var.k8s-worker-key }" +} + data "template_file" "worker-cloud-config" { template = "${ file( "${ path.module }/worker-cloud-config.yml" )}" @@ -8,8 +28,10 @@ data "template_file" "worker-cloud-config" { kubelet_image_tag = "${ var.kubelet_image_tag }" internal_tld = "${ var.internal_tld }" location = "${ var.location }" - k8s-worker-tar = "${ base64encode(var.k8s-worker-tar) }" - cloud-config = "${ base64encode(var.cloud-config) }" + cloud-config = "${ gzip_me.cloud-config }" + ca = "${ gzip_me.ca.output }" + k8s-worker = "${ gzip_me.k8s-worker.output }" + k8s-worker-key = "${ gzip_me.k8s-worker-key.output }" } } @@ -64,57 +86,3 @@ resource "azurerm_virtual_machine" "cncf" { } } } - -/* -resource "azurerm_virtual_machine_scale_set" "cncf" { - name = "${ var.name }" - location = "${ var.location }" - resource_group_name = "${ var.name }" - upgrade_policy_mode = "Manual" - - sku { - name = "Standard_A0" - tier = "Standard" - capacity = 2 - } - - os_profile { - computer_name_prefix = "worker" - admin_username = "${ var.admin_username }" - admin_password = "Password1234" - custom_data = "${ data.template_file.cloud-config.rendered }" - } - - os_profile_linux_config { - disable_password_authentication = true - ssh_keys { - path = "/home/${ var.admin_username }/.ssh/authorized_keys" - key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" - } - } - - network_profile { - name = "TestNetworkProfile" - primary = true - ip_configuration { - name = "TestIPConfiguration" - subnet_id = "${ var.subnet-id }" - # load_balancer_backend_address_pool_ids = ["${ var.external-lb }"] - } - } - - storage_profile_os_disk { - name = "osDiskProfile" - caching = "ReadWrite" - create_option = "FromImage" - vhd_containers = ["${ var.storage-primary-endpoint }${ var.storage-container }"] - } - - storage_profile_image_reference { - publisher = "CoreOS" - offer = "CoreOS" - sku = "Stable" - version = "1298.6.0" - } -} -*/ From d63d8afd052b285f15dc044216a57a38ea01b2ab Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 17 Apr 2017 08:59:27 +1200 Subject: [PATCH 025/149] Write AWS Kubeconf Per Deploy --- Dockerfile | 2 -- aws/cleanup.tf | 8 +------ aws/modules/kubeconfig/io.tf | 1 - aws/modules/kubeconfig/kubeconfig.tf | 34 ++++------------------------ 4 files changed, 5 insertions(+), 40 deletions(-) diff --git a/Dockerfile b/Dockerfile index fb2b7e9..2355f1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,8 +6,6 @@ ENV AWSCLI_VERSION=1.11.75 ENV AZURECLI_VERSION=2.0.2 ENV TERRAFORM_VERSION=0.9.0-beta2 ENV ARC=amd64 -ENV AWS_CONFIG_FILE=/cncf/data/awsconfig -ENV KUBECONFIG=/cncf/data/kubeconfig # Install AWS / AZURE CLI Deps RUN apk update diff --git a/aws/cleanup.tf b/aws/cleanup.tf index 659892a..adcebaf 100644 --- a/aws/cleanup.tf +++ b/aws/cleanup.tf @@ -6,13 +6,7 @@ resource "null_resource" "cleanup" { on_failure = "continue" command = < ${ var.data_dir }/kubeconfig -${data.template_file.kubeconfig.rendered} -__USERDATA__ -LOCAL_EXEC - } - - provisioner "local-exec" { - command = < Date: Mon, 17 Apr 2017 09:02:05 +1200 Subject: [PATCH 026/149] Destroy is now Done in Cleanup.tf --- aws/cert.tf | 9 --------- 1 file changed, 9 deletions(-) diff --git a/aws/cert.tf b/aws/cert.tf index 0c3dd3a..d73951d 100644 --- a/aws/cert.tf +++ b/aws/cert.tf @@ -11,15 +11,6 @@ ${ var.k8s_service_ip } EOF } - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = < Date: Mon, 17 Apr 2017 09:03:25 +1200 Subject: [PATCH 027/149] Fix Syntax --- aws/cert.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/cert.tf b/aws/cert.tf index d73951d..d87b469 100644 --- a/aws/cert.tf +++ b/aws/cert.tf @@ -10,6 +10,7 @@ ${ var.internal_tld } \ ${ var.k8s_service_ip } EOF } +} resource "null_resource" "dummy_dependency" { depends_on = [ "null_resource.ssl_gen" ] From 9ba190c2fd783b3d416c3f79c9b87b182dc2dcf7 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 09:04:41 +1200 Subject: [PATCH 028/149] merged aws into azure --- aws/modules.tf | 84 +++++++------- aws/modules/bastion/ec2.tf | 12 +- aws/modules/bastion/io.tf | 12 -- aws/modules/etcd/cloud-config.tf | 30 ++--- aws/modules/etcd/cloud-config.yml | 10 +- aws/modules/etcd/ec2.tf | 14 +-- aws/modules/etcd/elb.tf | 4 +- aws/modules/etcd/input.tf | 32 +++--- aws/modules/etcd/kube-apiserver.yml | 2 +- aws/modules/etcd/output.tf | 6 +- aws/modules/iam/io.tf | 8 +- aws/modules/kubeconfig/io.tf | 8 +- aws/modules/kubeconfig/kubeconfig.tf | 30 ++++- aws/modules/route53/io.tf | 8 +- aws/modules/route53/route53.tf | 2 +- aws/modules/security/io.tf | 10 +- aws/modules/security/security.tf | 10 +- aws/modules/vpc/io.tf | 13 --- aws/modules/worker/cloud-config.tf | 12 +- aws/modules/worker/cloud-config.yml | 4 +- aws/modules/worker/ec2.tf | 16 +-- aws/modules/worker/input.tf | 22 ++-- aws/modules/worker/output.tf | 2 +- aws/output.tf | 16 ++- aws/wait-for-cluster | 2 +- azure/docs/research.org | 2 +- azure/init-cfssl | 10 +- azure/input.tf | 5 +- azure/modules.tf | 109 ++++++++----------- azure/modules/bastion/bastion-node.tf | 8 +- azure/modules/bastion/input.tf | 12 +- azure/modules/bastion/output.tf | 2 +- azure/modules/dns/dns.tf | 18 +-- azure/modules/dns/input.tf | 8 +- azure/modules/dns/output.tf | 8 +- azure/modules/etcd/etcd-cloud-config.yml | 10 +- azure/modules/etcd/etcd-nodes.tf | 42 +++---- azure/modules/etcd/input.tf | 24 ++-- azure/modules/etcd/output.tf | 6 +- azure/modules/kubeconfig/kubeconfig.tf | 22 ++-- azure/modules/vpc/input.tf | 2 +- azure/modules/vpc/output.tf | 2 +- azure/modules/vpc/virtual_network.tf | 4 +- azure/modules/worker/input.tf | 16 +-- azure/modules/worker/worker-cloud-config.yml | 6 +- azure/modules/worker/worker-nodes.tf | 26 ++--- azure/output.tf | 27 +---- azure/readme.org | 2 +- azure/wait-for-cluster | 2 +- 49 files changed, 355 insertions(+), 387 deletions(-) delete mode 100644 aws/modules/bastion/io.tf delete mode 100644 aws/modules/vpc/io.tf diff --git a/aws/modules.tf b/aws/modules.tf index 3fdaa23..b78198f 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -1,6 +1,6 @@ module "vpc" { source = "./modules/vpc" - depends-id = "" + depends_id = "" azs = "${ var.aws_azs }" cidr = "${ var.vpc_cidr }" @@ -15,13 +15,13 @@ module "security" { allow_ssh_cidr = "${ var.allow_ssh_cidr }" vpc_cidr = "${ var.vpc_cidr }" name = "${ var.name }" - vpc-id = "${ module.vpc.id }" + vpc_id = "${ module.vpc.id }" } module "iam" { source = "./modules/iam" - # depends-id = "${ module.s3.depends-id }" + # depends_id = "${ module.s3.depends_id }" # s3_bucket = "${ module.s3.bucket }" name = "${ var.name }" } @@ -33,59 +33,59 @@ module "route53" { etcd_ips = "${ var.etcd_ips }" internal_tld = "${ var.internal_tld }" name = "${ var.name }" - vpc-id = "${ module.vpc.id }" + vpc_id = "${ module.vpc.id }" } module "etcd" { source = "./modules/etcd" - depends-id = "${ module.route53.depends-id }" - instance-profile-name = "${ module.iam.instance-profile-name-master }" + depends_id = "${ module.route53.depends_id }" + instance_profile_name = "${ module.iam.instance_profile_name_master }" - ami-id = "${ var.aws_image_ami }" + ami_id = "${ var.aws_image_ami }" cluster_domain = "${ var.cluster_domain }" kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" dns_service_ip = "${ var.dns_service_ip }" etcd_ips = "${ var.etcd_ips }" - etcd-security-group-id = "${ module.security.etcd-id }" - external-elb-security-group-id = "${ module.security.external-elb-id }" - instance-type = "${ var.aws_master_vm_size }" + etcd_security_group_id = "${ module.security.etcd_id }" + external_elb_security_group_id = "${ module.security.external_elb_id }" + instance_type = "${ var.aws_master_vm_size }" internal_tld = "${ var.internal_tld }" - key-name = "${ var.aws_key_name }" + key_name = "${ var.aws_key_name }" name = "${ var.name }" - pod-ip-range = "${ var.pod_cidr }" + pod_cidr = "${ var.pod_cidr }" region = "${ var.aws_region }" - service-cluster-ip-range = "${ var.service_cidr }" - subnet-ids-private = "${ module.vpc.subnet-ids-private }" - subnet-ids-public = "${ module.vpc.subnet-ids-public }" - vpc-id = "${ module.vpc.id }" + service_cidr = "${ var.service_cidr }" + subnet_ids_private = "${ module.vpc.subnet_ids_private }" + subnet_ids_public = "${ module.vpc.subnet_ids_public }" + vpc_id = "${ module.vpc.id }" ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" - k8s-etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" - k8s-etcd-key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" - k8s-apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" - k8s-apiserver-key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" + k8s_etcd = "${file("${ var.data_dir }/.cfssl/k8s_etcd.pem")}" + k8s_etcd_key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" + k8s_apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" + k8s_apiserver_key = "${file("${ var.data_dir }/.cfssl/k8s_apiserver_key.pem")}" } module "bastion" { source = "./modules/bastion" - depends-id = "${ module.etcd.depends-id }" + depends_id = "${ module.etcd.depends_id }" - ami-id = "${ var.aws_image_ami }" - instance-type = "${ var.aws_bastion_vm_size }" + ami_id = "${ var.aws_image_ami }" + instance_type = "${ var.aws_bastion_vm_size }" internal_tld = "${ var.internal_tld }" - key-name = "${ var.aws_key_name }" + key_name = "${ var.aws_key_name }" name = "${ var.name }" - security-group-id = "${ module.security.bastion-id }" - subnet-ids = "${ module.vpc.subnet-ids-public }" - vpc-id = "${ module.vpc.id }" + security_group_id = "${ module.security.bastion_id }" + subnet_ids = "${ module.vpc.subnet_ids_public }" + vpc_id = "${ module.vpc.id }" } module "worker" { source = "./modules/worker" - depends-id = "${ module.route53.depends-id }" - instance-profile-name = "${ module.iam.instance-profile-name-worker }" + depends_id = "${ module.route53.depends_id }" + instance_profile_name = "${ module.iam.instance_profile_name_worker }" - ami-id = "${ var.aws_image_ami }" + ami_id = "${ var.aws_image_ami }" capacity = { desired = 3 max = 5 @@ -95,32 +95,32 @@ module "worker" { kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" dns_service_ip = "${ var.dns_service_ip }" - instance-type = "${ var.aws_worker_vm_size }" + instance_type = "${ var.aws_worker_vm_size }" internal_tld = "${ var.internal_tld }" - key-name = "${ var.aws_key_name }" + key_name = "${ var.aws_key_name }" name = "${ var.name }" region = "${ var.aws_region }" - security-group-id = "${ module.security.worker-id }" - subnet-ids = "${ module.vpc.subnet-ids-private }" + security_group_id = "${ module.security.worker_id }" + subnet_ids = "${ module.vpc.subnet_ids_private }" ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" - k8s-worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" - k8s-worker-key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" + k8s_worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" + k8s_worker_key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" volume_size = { ebs = 250 root = 52 } - vpc-id = "${ module.vpc.id }" - worker-name = "general" + vpc_id = "${ module.vpc.id }" + worker_name = "general" } module "kubeconfig" { source = "./modules/kubeconfig" - admin-key-pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" - admin-pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" - ca-pem = "${ var.data_dir }/.cfssl/ca.pem" + admin_key_pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" + admin_pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" + ca_pem = "${ var.data_dir }/.cfssl/ca.pem" data_dir = "${ var.data_dir }" - master-elb = "${ module.etcd.external-elb }" + master_elb = "${ module.etcd.external_elb }" name = "${ var.name }" } diff --git a/aws/modules/bastion/ec2.tf b/aws/modules/bastion/ec2.tf index dcd14c2..013f739 100644 --- a/aws/modules/bastion/ec2.tf +++ b/aws/modules/bastion/ec2.tf @@ -1,18 +1,18 @@ resource "aws_instance" "bastion" { - ami = "${ var.ami-id }" + ami = "${ var.ami_id }" associate_public_ip_address = true - instance_type = "${ var.instance-type }" - key_name = "${ var.key-name }" + instance_type = "${ var.instance_type }" + key_name = "${ var.key_name }" # TODO: force private_ip to prevent collision with etcd machines source_dest_check = false - subnet_id = "${ element( split(",", var.subnet-ids), 0 ) }" + subnet_id = "${ element( split(",", var.subnet_ids), 0 ) }" tags { builtWith = "terraform" kz8s = "${ var.name }" - depends-id = "${ var.depends-id }" + depends-id = "${ var.depends_id }" Name = "kz8s-bastion" role = "bastion" } @@ -20,7 +20,7 @@ resource "aws_instance" "bastion" { user_data = "${ data.template_file.user-data.rendered }" vpc_security_group_ids = [ - "${ var.security-group-id }", + "${ var.security_group_id }", ] } diff --git a/aws/modules/bastion/io.tf b/aws/modules/bastion/io.tf deleted file mode 100644 index ffca7bd..0000000 --- a/aws/modules/bastion/io.tf +++ /dev/null @@ -1,12 +0,0 @@ -variable "ami-id" {} -variable "depends-id" {} -variable "instance-type" {} -variable "internal_tld" {} -variable "key-name" {} -variable "name" {} -variable "security-group-id" {} -variable "subnet-ids" {} -variable "vpc-id" {} - -output "depends-id" { value = "${null_resource.dummy_dependency.id}" } -output "ip" { value = "${ aws_instance.bastion.public_ip }" } diff --git a/aws/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf index 66b2273..ef4ef1c 100644 --- a/aws/modules/etcd/cloud-config.tf +++ b/aws/modules/etcd/cloud-config.tf @@ -6,20 +6,20 @@ resource "gzip_me" "ca" { input = "${ var.ca }" } -resource "gzip_me" "k8s-etcd" { - input = "${ var.k8s-etcd }" +resource "gzip_me" "k8s_etcd" { + input = "${ var.k8s_etcd }" } -resource "gzip_me" "k8s-etcd-key" { - input = "${ var.k8s-etcd-key }" +resource "gzip_me" "k8s_etcd_key" { + input = "${ var.k8s_etcd_key }" } -resource "gzip_me" "k8s-apiserver" { - input = "${ var.k8s-apiserver }" +resource "gzip_me" "k8s_apiserver" { + input = "${ var.k8s_apiserver }" } -resource "gzip_me" "k8s-apiserver-key" { - input = "${ var.k8s-apiserver-key }" +resource "gzip_me" "k8s_apiserver_key" { + input = "${ var.k8s_apiserver_key }" } data "template_file" "kube-apiserver" { @@ -27,7 +27,7 @@ data "template_file" "kube-apiserver" { vars { internal_tld = "${ var.internal_tld }" - service-cluster-ip-range = "${ var.service-cluster-ip-range }" + service_cidr = "${ var.service_cidr }" hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" @@ -52,14 +52,14 @@ data "template_file" "cloud-config" { kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" internal_tld = "${ var.internal_tld }" - pod-ip-range = "${ var.pod-ip-range }" + pod_cidr = "${ var.pod_cidr }" region = "${ var.region }" - service-cluster-ip-range = "${ var.service-cluster-ip-range }" + service_cidr = "${ var.service_cidr }" ca = "${ gzip_me.ca.output }" - k8s-etcd = "${ gzip_me.k8s-etcd.output }" - k8s-etcd-key = "${ gzip_me.k8s-etcd-key.output }" - k8s-apiserver = "${ gzip_me.k8s-apiserver.output }" - k8s-apiserver-key = "${ gzip_me.k8s-apiserver-key.output }" + k8s_etcd = "${ gzip_me.k8s_etcd.output }" + k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" + k8s_apiserver = "${ gzip_me.k8s_apiserver.output }" + k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" kube-apiserver-yml = "${ gzip_me.kube-apiserver.output }" } } diff --git a/aws/modules/etcd/cloud-config.yml b/aws/modules/etcd/cloud-config.yml index 682b490..b8da339 100644 --- a/aws/modules/etcd/cloud-config.yml +++ b/aws/modules/etcd/cloud-config.yml @@ -31,7 +31,7 @@ coreos: content: | [Service] ExecStartPre=-/usr/bin/etcdctl mk /coreos.com/network/config \ - '{ "Network": "${ pod-ip-range }", "Backend": { "Type": "vxlan" } }' + '{ "Network": "${ pod_cidr }", "Backend": { "Type": "vxlan" } }' Restart=always RestartSec=10 @@ -226,23 +226,23 @@ write-files: permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-etcd } + ${ k8s_etcd } - path: /etc/kubernetes/ssl/k8s-etcd-key.pem permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-etcd-key } + ${ k8s_etcd_key } - path: /etc/kubernetes/ssl/k8s-apiserver.pem permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-apiserver } + ${ k8s_apiserver } - path: /etc/kubernetes/ssl/k8s-apiserver-key.pem permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-apiserver-key } + ${ k8s_apiserver_key } diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 2a6536c..935f34e 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -1,11 +1,11 @@ resource "aws_instance" "etcd" { count = "${ length( split(",", var.etcd_ips) ) }" - ami = "${ var.ami-id }" + ami = "${ var.ami_id }" associate_public_ip_address = false - iam_instance_profile = "${ var.instance-profile-name }" - instance_type = "${ var.instance-type }" - key_name = "${ var.key-name }" + iam_instance_profile = "${ var.instance_profile_name }" + instance_type = "${ var.instance_type }" + key_name = "${ var.key_name }" private_ip = "${ element(split(",", var.etcd_ips), count.index) }" root_block_device { @@ -14,11 +14,11 @@ resource "aws_instance" "etcd" { } source_dest_check = false - subnet_id = "${ element( split(",", var.subnet-ids-private), 0 ) }" + subnet_id = "${ element( split(",", var.subnet_ids_private), 0 ) }" tags { builtWith = "terraform" - depends-id = "${ var.depends-id }" + depends-id = "${ var.depends_id }" KubernetesCluster = "${ var.name }" # used by kubelet's aws provider to determine cluster kz8s = "${ var.name }" Name = "etcd${ count.index + 1 }-${ var.name }" @@ -28,7 +28,7 @@ resource "aws_instance" "etcd" { } user_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" - vpc_security_group_ids = [ "${ var.etcd-security-group-id }" ] + vpc_security_group_ids = [ "${ var.etcd_security_group_id }" ] } resource "null_resource" "dummy_dependency" { diff --git a/aws/modules/etcd/elb.tf b/aws/modules/etcd/elb.tf index 078d576..33e21f0 100644 --- a/aws/modules/etcd/elb.tf +++ b/aws/modules/etcd/elb.tf @@ -21,8 +21,8 @@ resource "aws_elb" "external" { lb_protocol = "tcp" } - security_groups = [ "${ var.external-elb-security-group-id }" ] - subnets = [ "${ split(",", var.subnet-ids-public) }" ] + security_groups = [ "${ var.external_elb_security_group_id }" ] + subnets = [ "${ split(",", var.subnet_ids_public) }" ] tags { builtWith = "terraform" diff --git a/aws/modules/etcd/input.tf b/aws/modules/etcd/input.tf index 9f96d57..e0ca731 100644 --- a/aws/modules/etcd/input.tf +++ b/aws/modules/etcd/input.tf @@ -1,26 +1,26 @@ -variable "ami-id" {} +variable "ami_id" {} variable "cluster_domain" {} variable "kubelet_aci" {} variable "kubelet_version" {} -variable "depends-id" {} +variable "depends_id" {} variable "dns_service_ip" {} variable "etcd_ips" {} -variable "etcd-security-group-id" {} -variable "external-elb-security-group-id" {} -variable "instance-type" {} +variable "etcd_security_group_id" {} +variable "external_elb_security_group_id" {} +variable "instance_type" {} variable "internal_tld" {} -variable "key-name" {} +variable "key_name" {} variable "name" {} -variable "pod-ip-range" {} +variable "pod_cidr" {} variable "region" {} -variable "service-cluster-ip-range" {} -variable "subnet-ids-private" {} -variable "subnet-ids-public" {} -variable "vpc-id" {} +variable "service_cidr" {} +variable "subnet_ids_private" {} +variable "subnet_ids_public" {} +variable "vpc_id" {} variable "ca" {} -variable "k8s-etcd" {} -variable "k8s-etcd-key" {} -variable "k8s-apiserver" {} -variable "k8s-apiserver-key" {} +variable "k8s_etcd" {} +variable "k8s_etcd_key" {} +variable "k8s_apiserver" {} +variable "k8s_apiserver_key" {} # variable "s3_bucket" {} -variable "instance-profile-name" {} +variable "instance_profile_name" {} diff --git a/aws/modules/etcd/kube-apiserver.yml b/aws/modules/etcd/kube-apiserver.yml index 331d58f..a799bdb 100644 --- a/aws/modules/etcd/kube-apiserver.yml +++ b/aws/modules/etcd/kube-apiserver.yml @@ -24,7 +24,7 @@ spec: - --insecure-bind-address=0.0.0.0 - --secure-port=443 - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --service-cluster-ip-range=${ service-cluster-ip-range } + - --service-cluster-ip-range=${ service_cidr } - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - --v=2 diff --git a/aws/modules/etcd/output.tf b/aws/modules/etcd/output.tf index 28ed7eb..74c1fb0 100644 --- a/aws/modules/etcd/output.tf +++ b/aws/modules/etcd/output.tf @@ -1,4 +1,4 @@ -output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } -output "external-elb" { value = "${ aws_elb.external.dns_name }" } -output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } +output "depends_id" { value = "${ null_resource.dummy_dependency.id }" } +output "external_elb" { value = "${ aws_elb.external.dns_name }" } +output "internal_ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } diff --git a/aws/modules/iam/io.tf b/aws/modules/iam/io.tf index 1e7512c..3bae317 100644 --- a/aws/modules/iam/io.tf +++ b/aws/modules/iam/io.tf @@ -1,9 +1,9 @@ # variable "s3_bucket" {} -# variable "depends-id" {} +# variable "depends_id" {} variable "name" {} -output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } +output "depends_id" { value = "${ null_resource.dummy_dependency.id }" } output "aws-iam-role-etcd-id" { value = "${ aws_iam_role.master.id }" } output "aws-iam-role-worker-id" { value = "${ aws_iam_role.worker.id }" } -output "instance-profile-name-master" { value = "${ aws_iam_instance_profile.master.name }" } -output "instance-profile-name-worker" { value = "${ aws_iam_instance_profile.worker.name }" } +output "instance_profile_name_master" { value = "${ aws_iam_instance_profile.master.name }" } +output "instance_profile_name_worker" { value = "${ aws_iam_instance_profile.worker.name }" } diff --git a/aws/modules/kubeconfig/io.tf b/aws/modules/kubeconfig/io.tf index ed19edd..ff6ddc0 100644 --- a/aws/modules/kubeconfig/io.tf +++ b/aws/modules/kubeconfig/io.tf @@ -1,7 +1,7 @@ -variable "admin-key-pem" {} -variable "admin-pem" {} -variable "ca-pem" {} -variable "master-elb" {} +variable "admin_key_pem" {} +variable "admin_pem" {} +variable "ca_pem" {} +variable "master_elb" {} variable "name" {} variable "data_dir" {} diff --git a/aws/modules/kubeconfig/kubeconfig.tf b/aws/modules/kubeconfig/kubeconfig.tf index 7d87897..44395dd 100644 --- a/aws/modules/kubeconfig/kubeconfig.tf +++ b/aws/modules/kubeconfig/kubeconfig.tf @@ -1,3 +1,25 @@ +data "template_file" "kubeconfig" { + template = < ${ var.name-servers-file }.${ count.index }.ip +dig +short ${ element(azurerm_dns_zone.cncf.name_servers,count.index) } > ${ var.name_servers_file }.${ count.index }.ip EOF } } @@ -28,10 +28,10 @@ resource "null_resource" "dns_gen" { # collect them all into a csv # wait for them all to appear sleep 4 -cat ${ var.name-servers-file }.*.ip \ +cat ${ var.name_servers_file }.*.ip \ | sed -n -e 'H;$${x;s/\n/,/g;s/^,//;p;}' \ | tr -d '\n' \ -> ${ var.name-servers-file} +> ${ var.name_servers_file} EOF } } @@ -42,19 +42,19 @@ resource "azurerm_dns_a_record" "A-etcd" { resource_group_name = "${ var.name }" ttl = "300" records = [ - "${ var.master-ips }" + "${ var.master_ips }" ] } resource "azurerm_dns_a_record" "A-etcds" { - count = "${ length(var.master-ips) }" + count = "${ length(var.master_ips) }" name = "etcd${ count.index+1 }" zone_name = "${azurerm_dns_zone.cncf.name}" resource_group_name = "${ var.name }" ttl = "300" records = [ - "${ element(var.master-ips, count.index) }" + "${ element(var.master_ips, count.index) }" ] } @@ -63,17 +63,17 @@ resource "azurerm_dns_a_record" "A-master" { zone_name = "${azurerm_dns_zone.cncf.name}" resource_group_name = "${ var.name }" ttl = "300" - records = [ "${ var.master-ips }" ] + records = [ "${ var.master_ips }" ] } resource "azurerm_dns_a_record" "A-masters" { - count = "${ length(var.master-ips) }" + count = "${ length(var.master_ips) }" name = "master${ count.index+1 }" zone_name = "${azurerm_dns_zone.cncf.name}" resource_group_name = "${ var.name }" ttl = "300" records = [ - "${ element(var.master-ips, count.index) }" + "${ element(var.master_ips, count.index) }" ] } diff --git a/azure/modules/dns/input.tf b/azure/modules/dns/input.tf index 34a21c6..f29b598 100644 --- a/azure/modules/dns/input.tf +++ b/azure/modules/dns/input.tf @@ -1,6 +1,6 @@ -# variable "depends-id" {} +# variable "depends_id" {} variable "internal_tld" {} variable "name" {} -variable "name-servers-file" { default = "azure_dns_zone"} -variable "master-ips" { type = "list" } -# variable "vpc-id" {} +variable "name_servers_file" { default = "azure_dns_zone"} +variable "master_ips" { type = "list" } +# variable "vpc_id" {} diff --git a/azure/modules/dns/output.tf b/azure/modules/dns/output.tf index e1809b4..74d6c33 100644 --- a/azure/modules/dns/output.tf +++ b/azure/modules/dns/output.tf @@ -1,4 +1,4 @@ -# output "depends-id" { value = "${null_resource.dummy_dependency.id}" } -output "internal-name-servers" { value = "${ azurerm_dns_zone.cncf.name_servers }" } -output "internal-zone-id" { value = "${ azurerm_dns_zone.cncf.zone_id }" } -output "name-servers-file" { value = "${ var.name-servers-file }" } +# output "depends_id" { value = "${null_resource.dummy_dependency.id}" } +output "internal_name_servers" { value = "${ azurerm_dns_zone.cncf.name_servers }" } +output "internal_zone_id" { value = "${ azurerm_dns_zone.cncf.zone_id }" } +output "name_servers_file" { value = "${ var.name_servers_file }" } diff --git a/azure/modules/etcd/etcd-cloud-config.yml b/azure/modules/etcd/etcd-cloud-config.yml index b378272..7bfc561 100644 --- a/azure/modules/etcd/etcd-cloud-config.yml +++ b/azure/modules/etcd/etcd-cloud-config.yml @@ -206,28 +206,28 @@ write-files: permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-etcd } + ${ k8s_etcd } - path: /etc/kubernetes/ssl/k8s-etcd-key.pem permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-etcd-key } + ${ k8s_etcd_key } - path: /etc/kubernetes/ssl/k8s-apiserver.pem permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-apiserver } + ${ k8s_apiserver } - path: /etc/kubernetes/ssl/k8s-apiserver-key.pem permissions: "0644" encoding: "gzip+base64" content: | - ${ k8s-apiserver-key } + ${ k8s_apiserver_key } - path: /etc/kubernetes/ssl/azure-config.json permissions: "0644" encoding: "gzip+base64" content: | - ${ cloud-config } + ${ cloud_config } diff --git a/azure/modules/etcd/etcd-nodes.tf b/azure/modules/etcd/etcd-nodes.tf index 7896985..b02d7f6 100644 --- a/azure/modules/etcd/etcd-nodes.tf +++ b/azure/modules/etcd/etcd-nodes.tf @@ -6,7 +6,7 @@ resource "azurerm_network_interface" "cncf" { ip_configuration { name = "etcd-nic${ count.index + 1 }" - subnet_id = "${ var.subnet-id }" + subnet_id = "${ var.subnet_id }" private_ip_address_allocation = "dynamic" # private_ip_address = "${ element( split(",", var.etcd-ips), count.index ) }" load_balancer_backend_address_pools_ids = ["${ azurerm_lb_backend_address_pool.cncf.id }"] @@ -17,7 +17,7 @@ resource "azurerm_virtual_machine" "cncf" { count = "${ var.master_node_count }" name = "etcd-master${ count.index + 1 }" location = "${ var.location }" - availability_set_id = "${ var.availability-id }" + availability_set_id = "${ var.availability_id }" resource_group_name = "${ var.name }" network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] vm_size = "${ var.master_vm_size }" @@ -31,7 +31,7 @@ resource "azurerm_virtual_machine" "cncf" { storage_os_disk { name = "etcd-disks${ count.index + 1 }" - vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/etcd-vhd${ count.index + 1 }.vhd" + vhd_uri = "${ var.storage_primary_endpoint }${ var.storage_container }/etcd-vhd${ count.index + 1 }.vhd" caching = "ReadWrite" create_option = "FromImage" } @@ -40,14 +40,14 @@ resource "azurerm_virtual_machine" "cncf" { computer_name = "etcd-master${ count.index + 1 }" admin_username = "${ var.admin_username }" admin_password = "Password1234!" - custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" + custom_data = "${ element(data.template_file.cloud_config.*.rendered, count.index) }" } os_profile_linux_config { disable_password_authentication = true ssh_keys { path = "/home/${ var.admin_username }/.ssh/authorized_keys" - key_data = "${file("${var.data_dir}.ssh/id_rsa.pub")}" + key_data = "${file("${ var.data_dir }/.ssh/id_rsa.pub")}" } } } @@ -59,35 +59,35 @@ provider "gzip" { resource "gzip_me" "kube-apiserver" { input = "${ data.template_file.kube-apiserver.rendered }" } -resource "gzip_me" "cloud-config" { - input = "${ var.cloud-config }" +resource "gzip_me" "cloud_config" { + input = "${ var.cloud_config }" } resource "gzip_me" "ca" { input = "${ var.ca }" } -resource "gzip_me" "k8s-etcd" { - input = "${ var.k8s-etcd }" +resource "gzip_me" "k8s_etcd" { + input = "${ var.k8s_etcd }" } -resource "gzip_me" "k8s-etcd-key" { - input = "${ var.k8s-etcd-key }" +resource "gzip_me" "k8s_etcd_key" { + input = "${ var.k8s_etcd_key }" } -resource "gzip_me" "k8s-apiserver" { - input = "${ var.k8s-apiserver }" +resource "gzip_me" "k8s_apiserver" { + input = "${ var.k8s_apiserver }" } -resource "gzip_me" "k8s-apiserver-key" { - input = "${ var.k8s-apiserver-key }" +resource "gzip_me" "k8s_apiserver_key" { + input = "${ var.k8s_apiserver_key }" } data "template_file" "kube-apiserver" { template = "${ file( "${ path.module }/kube-apiserver.yml" )}" vars { internal_tld = "${ var.internal_tld }" - service-cluster-ip-range = "${ var.service-cluster-ip-range }" + service_cidr = "${ var.service_cidr }" hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" kubelet_aci = "${ var.kubelet_aci }" kubelet_version = "${ var.kubelet_version }" @@ -111,12 +111,12 @@ data "template_file" "etcd-cloud-config" { pod_cidr = "${ var.pod_cidr }" location = "${ var.location }" service_cidr = "${ var.service_cidr }" - cloud-config = "${ gzip_me.cloud-config }" + cloud_config = "${ gzip_me.cloud_config }" ca = "${ gzip_me.ca.output }" - k8s-etcd = "${ gzip_me.k8s-etcd.output }" - k8s-etcd-key = "${ gzip_me.k8s-etcd-key.output }" - k8s-apiserver = "${ gzip_me.k8s-apiserver.output }" - k8s-apiserver-key = "${ gzip_me.k8s-apiserver.output }" + k8s_etcd = "${ gzip_me.k8s_etcd.output }" + k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" + k8s_apiserver = "${ gzip_me.k8s_apiserver.output }" + k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" k8s-apiserver-yml = "${ gzip_me.kube-apiserver.output }" node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" diff --git a/azure/modules/etcd/input.tf b/azure/modules/etcd/input.tf index e82be8e..da72558 100644 --- a/azure/modules/etcd/input.tf +++ b/azure/modules/etcd/input.tf @@ -1,5 +1,5 @@ variable "location" {} -variable "subnet-id" {} +variable "subnet_id" {} variable "name" {} variable "master_vm_size" {} variable "master_node_count" {} @@ -7,10 +7,10 @@ variable "image_publisher" {} variable "image_offer" {} variable "image_sku" {} variable "image_version" {} -variable "availability-id" {} -variable "storage-account" {} -variable "storage-primary-endpoint" {} -variable "storage-container" {} +variable "availability_id" {} +variable "storage_account" {} +variable "storage_primary_endpoint" {} +variable "storage_container" {} variable "k8s-apiserver-tar" {} variable "cluster_domain" {} variable "dns_service_ip" {} @@ -20,11 +20,11 @@ variable "service_cidr" {} variable "admin_username" {} variable "kubelet_image_url" {} variable "kubelet_image_tag" {} -variable "cloud-config" {} -# variable "etcd-security-group-id" {} -# variable "external-elb-security-group-id" {} +variable "cloud_config" {} +# variable "etcd_security_group_id" {} +# variable "external_elb_security_group_id" {} variable "ca" {} -variable "k8s-etcd" {} -variable "k8s-etcd-key" {} -variable "k8s-apiserver" {} -variable "k8s-apiserver-key" {} +variable "k8s_etcd" {} +variable "k8s_etcd_key" {} +variable "k8s_apiserver" {} +variable "k8s_apiserver_key" {} diff --git a/azure/modules/etcd/output.tf b/azure/modules/etcd/output.tf index 955bae8..19f77f4 100644 --- a/azure/modules/etcd/output.tf +++ b/azure/modules/etcd/output.tf @@ -1,3 +1,3 @@ -output "external-lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } -output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } -output "master-ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } +output "external_lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } +output "fqdn_lb" { value = "${azurerm_public_ip.cncf.fqdn}" } +output "master_ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } diff --git a/azure/modules/kubeconfig/kubeconfig.tf b/azure/modules/kubeconfig/kubeconfig.tf index 90c3a7b..445c51c 100644 --- a/azure/modules/kubeconfig/kubeconfig.tf +++ b/azure/modules/kubeconfig/kubeconfig.tf @@ -2,14 +2,14 @@ data "template_file" "kubeconfig" { template = < ./tmp/kubeconfig +mkdir -p ./tmp && cat <<'__USERDATA__' > ${ var.data_dir }/kubeconfig ${data.template_file.kubeconfig.rendered} __USERDATA__ LOCAL_EXEC @@ -34,12 +34,12 @@ LOCAL_EXEC provisioner "local-exec" { command = < Date: Mon, 17 Apr 2017 09:12:21 +1200 Subject: [PATCH 029/149] Removing old kubeconfig approach --- aws/modules/kubeconfig/kubeconfig.tf | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/aws/modules/kubeconfig/kubeconfig.tf b/aws/modules/kubeconfig/kubeconfig.tf index 44395dd..7903204 100644 --- a/aws/modules/kubeconfig/kubeconfig.tf +++ b/aws/modules/kubeconfig/kubeconfig.tf @@ -1,26 +1,3 @@ -data "template_file" "kubeconfig" { - template = < Date: Mon, 17 Apr 2017 09:22:50 +1200 Subject: [PATCH 030/149] Continued refactor for Azure to match AWS --- aws/modules/bastion/input.tf | 9 +++ aws/modules/bastion/output.tf | 2 + aws/modules/vpc/input.tf | 7 ++ aws/modules/vpc/output.tf | 7 ++ azure/modules/etcd/api-server.yml | 60 +++++++++++++++++ azure/modules/etcd/etcd-cloud-config.tf | 70 ++++++++++++++++++++ azure/modules/etcd/etcd-nodes.tf | 71 --------------------- azure/modules/kubeconfig/input.tf | 6 ++ azure/modules/kubeconfig/output.tf | 1 + azure/modules/worker/worker-cloud-config.tf | 37 +++++++++++ azure/modules/worker/worker-nodes.tf | 37 ----------- 11 files changed, 199 insertions(+), 108 deletions(-) create mode 100644 aws/modules/bastion/input.tf create mode 100644 aws/modules/bastion/output.tf create mode 100644 aws/modules/vpc/input.tf create mode 100644 aws/modules/vpc/output.tf create mode 100644 azure/modules/etcd/api-server.yml create mode 100644 azure/modules/etcd/etcd-cloud-config.tf create mode 100644 azure/modules/kubeconfig/input.tf create mode 100644 azure/modules/kubeconfig/output.tf create mode 100644 azure/modules/worker/worker-cloud-config.tf diff --git a/aws/modules/bastion/input.tf b/aws/modules/bastion/input.tf new file mode 100644 index 0000000..c1ced18 --- /dev/null +++ b/aws/modules/bastion/input.tf @@ -0,0 +1,9 @@ +variable "ami_id" {} +variable "depends_id" {} +variable "instance_type" {} +variable "internal_tld" {} +variable "key_name" {} +variable "name" {} +variable "security_group_id" {} +variable "subnet_ids" {} +variable "vpc_id" {} diff --git a/aws/modules/bastion/output.tf b/aws/modules/bastion/output.tf new file mode 100644 index 0000000..94de6c7 --- /dev/null +++ b/aws/modules/bastion/output.tf @@ -0,0 +1,2 @@ +output "depends_id" { value = "${null_resource.dummy_dependency.id}" } +output "ip" { value = "${ aws_instance.bastion.public_ip }" } diff --git a/aws/modules/vpc/input.tf b/aws/modules/vpc/input.tf new file mode 100644 index 0000000..9dd0c58 --- /dev/null +++ b/aws/modules/vpc/input.tf @@ -0,0 +1,7 @@ +variable "azs" {} +variable "cidr" {} +variable "kubelet_version" {} +variable "depends_id" {} +variable "name" {} +variable "region" {} + diff --git a/aws/modules/vpc/output.tf b/aws/modules/vpc/output.tf new file mode 100644 index 0000000..583604e --- /dev/null +++ b/aws/modules/vpc/output.tf @@ -0,0 +1,7 @@ +output "depends_id" { value = "${null_resource.dummy_dependency.id}" } +output "id" { value = "${ aws_vpc.main.id }" } +output "subnet_ids_private" { value = "${ join(",", aws_subnet.private.*.id) }" } +output "subnet_ids_public" { value = "${ join(",", aws_subnet.public.*.id) }" } + +#output "gateway_id" { value = "${ aws_internet_gateway.main.id }" } +#output "route_table_id" { value = "${ aws_route_table.private.id }" } diff --git a/azure/modules/etcd/api-server.yml b/azure/modules/etcd/api-server.yml new file mode 100644 index 0000000..ba9f77a --- /dev/null +++ b/azure/modules/etcd/api-server.yml @@ -0,0 +1,60 @@ +-- +apiVersion: v1 +kind: Pod +metadata: + name: kube-apiserver + namespace: kube-system +spec: + hostNetwork: true + containers: + - name: kube-apiserver + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - apiserver + - --admission-control=LimitRanger + - --admission-control=NamespaceExists + - --admission-control=NamespaceLifecycle + - --admission-control=ResourceQuota + - --admission-control=SecurityContextDeny + - --admission-control=ServiceAccount + - --allow-privileged=true + - --client-ca-file=/etc/kubernetes/ssl/ca.pem + - --cloud-provider=azure + - --cloud-config=/etc/kubernetes/ssl/azure-config.json + - --etcd-servers=http://etcd.${ internal_tld }:2379 + - --insecure-bind-address=0.0.0.0 + - --secure-port=443 + - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --service-cluster-ip-range=${ service_cidr } + - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem + - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --v=2 + livenessProbe: + httpGet: + host: 127.0.0.1 + port: 8080 + path: /healthz + initialDelaySeconds: 15 + timeoutSeconds: 15 + ports: + - containerPort: 443 + hostPort: 443 + name: https + - containerPort: 8080 + hostPort: 8080 + name: local + volumeMounts: + - mountPath: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + readOnly: true + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host diff --git a/azure/modules/etcd/etcd-cloud-config.tf b/azure/modules/etcd/etcd-cloud-config.tf new file mode 100644 index 0000000..2bfbc20 --- /dev/null +++ b/azure/modules/etcd/etcd-cloud-config.tf @@ -0,0 +1,70 @@ +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "kube-apiserver" { + input = "${ data.template_file.kube-apiserver.rendered }" +} +resource "gzip_me" "cloud_config" { + input = "${ var.cloud_config }" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s_etcd" { + input = "${ var.k8s_etcd }" +} + +resource "gzip_me" "k8s_etcd_key" { + input = "${ var.k8s_etcd_key }" +} + +resource "gzip_me" "k8s_apiserver" { + input = "${ var.k8s_apiserver }" +} + +resource "gzip_me" "k8s_apiserver_key" { + input = "${ var.k8s_apiserver_key }" +} + +data "template_file" "kube-apiserver" { + template = "${ file( "${ path.module }/kube-apiserver.yml" )}" + vars { + internal_tld = "${ var.internal_tld }" + service_cidr = "${ var.service_cidr }" + hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" + kubelet_aci = "${ var.kubelet_aci }" + kubelet_version = "${ var.kubelet_version }" + } +} + +data "template_file" "etcd-cloud-config" { + count = "${ var.master_node_count }" + template = "${ file( "${ path.module }/etcd-cloud-config.yml" )}" + + vars { + # bucket = "${ var.s3_bucket }" + cluster_domain = "${ var.cluster_domain }" + cluster-token = "etcd-cluster-${ var.name }" + dns_service_ip = "${ var.dns_service_ip }" + fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" + hostname = "etcd${ count.index + 1 }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + internal_tld = "${ var.internal_tld }" + pod_cidr = "${ var.pod_cidr }" + location = "${ var.location }" + service_cidr = "${ var.service_cidr }" + cloud_config = "${ gzip_me.cloud_config }" + ca = "${ gzip_me.ca.output }" + k8s_etcd = "${ gzip_me.k8s_etcd.output }" + k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" + k8s_apiserver = "${ gzip_me.k8s_apiserver.output }" + k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" + k8s-apiserver-yml = "${ gzip_me.kube-apiserver.output }" + node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" + + } +} diff --git a/azure/modules/etcd/etcd-nodes.tf b/azure/modules/etcd/etcd-nodes.tf index b02d7f6..39d19ce 100644 --- a/azure/modules/etcd/etcd-nodes.tf +++ b/azure/modules/etcd/etcd-nodes.tf @@ -51,74 +51,3 @@ resource "azurerm_virtual_machine" "cncf" { } } } - -provider "gzip" { - compressionlevel = "BestCompression" -} - -resource "gzip_me" "kube-apiserver" { - input = "${ data.template_file.kube-apiserver.rendered }" -} -resource "gzip_me" "cloud_config" { - input = "${ var.cloud_config }" -} - -resource "gzip_me" "ca" { - input = "${ var.ca }" -} - -resource "gzip_me" "k8s_etcd" { - input = "${ var.k8s_etcd }" -} - -resource "gzip_me" "k8s_etcd_key" { - input = "${ var.k8s_etcd_key }" -} - -resource "gzip_me" "k8s_apiserver" { - input = "${ var.k8s_apiserver }" -} - -resource "gzip_me" "k8s_apiserver_key" { - input = "${ var.k8s_apiserver_key }" -} - -data "template_file" "kube-apiserver" { - template = "${ file( "${ path.module }/kube-apiserver.yml" )}" - vars { - internal_tld = "${ var.internal_tld }" - service_cidr = "${ var.service_cidr }" - hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" - } -} - -data "template_file" "etcd-cloud-config" { - count = "${ var.master_node_count }" - template = "${ file( "${ path.module }/etcd-cloud-config.yml" )}" - - vars { - # bucket = "${ var.s3_bucket }" - cluster_domain = "${ var.cluster_domain }" - cluster-token = "etcd-cluster-${ var.name }" - dns_service_ip = "${ var.dns_service_ip }" - fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" - hostname = "etcd${ count.index + 1 }" - kubelet_image_url = "${ var.kubelet_image_url }" - kubelet_image_tag = "${ var.kubelet_image_tag }" - internal_tld = "${ var.internal_tld }" - pod_cidr = "${ var.pod_cidr }" - location = "${ var.location }" - service_cidr = "${ var.service_cidr }" - cloud_config = "${ gzip_me.cloud_config }" - ca = "${ gzip_me.ca.output }" - k8s_etcd = "${ gzip_me.k8s_etcd.output }" - k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" - k8s_apiserver = "${ gzip_me.k8s_apiserver.output }" - k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" - k8s-apiserver-yml = "${ gzip_me.kube-apiserver.output }" - node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" - - } -} diff --git a/azure/modules/kubeconfig/input.tf b/azure/modules/kubeconfig/input.tf new file mode 100644 index 0000000..19ab85c --- /dev/null +++ b/azure/modules/kubeconfig/input.tf @@ -0,0 +1,6 @@ +variable "admin_key_pem" {} +variable "admin_pem" {} +variable "ca_pem" {} +variable "fqdn_k8s" {} +variable "name" {} +variable "data_dir" {} diff --git a/azure/modules/kubeconfig/output.tf b/azure/modules/kubeconfig/output.tf new file mode 100644 index 0000000..4e7f3d7 --- /dev/null +++ b/azure/modules/kubeconfig/output.tf @@ -0,0 +1 @@ +output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/azure/modules/worker/worker-cloud-config.tf b/azure/modules/worker/worker-cloud-config.tf new file mode 100644 index 0000000..ee8c2e1 --- /dev/null +++ b/azure/modules/worker/worker-cloud-config.tf @@ -0,0 +1,37 @@ +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "cloud_config" { + input = "${ var.cloud_config }" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s_worker" { + input = "${ var.k8s_worker }" +} + +resource "gzip_me" "k8s_worker_key" { + input = "${ var.k8s_worker_key }" +} + +data "template_file" "worker-cloud-config" { + template = "${ file( "${ path.module }/worker-cloud-config.yml" )}" + + vars { + cluster_domain = "${ var.cluster_domain }" + dns_service_ip = "${ var.dns_service_ip }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + internal_tld = "${ var.internal_tld }" + location = "${ var.location }" + cloud_config = "${ gzip_me.cloud_config }" + ca = "${ gzip_me.ca.output }" + k8s_worker = "${ gzip_me.k8s_worker.output }" + k8s_worker_key = "${ gzip_me.k8s_worker_key.output }" + } +} + diff --git a/azure/modules/worker/worker-nodes.tf b/azure/modules/worker/worker-nodes.tf index 5b1296b..d83c00c 100644 --- a/azure/modules/worker/worker-nodes.tf +++ b/azure/modules/worker/worker-nodes.tf @@ -1,40 +1,3 @@ -provider "gzip" { - compressionlevel = "BestCompression" -} - -resource "gzip_me" "cloud-config" { - input = "${ var.cloud_config }" -} - -resource "gzip_me" "ca" { - input = "${ var.ca }" -} - -resource "gzip_me" "k8s_worker" { - input = "${ var.k8s_worker }" -} - -resource "gzip_me" "k8s_worker_key" { - input = "${ var.k8s_worker_key }" -} - -data "template_file" "worker-cloud-config" { - template = "${ file( "${ path.module }/worker-cloud-config.yml" )}" - - vars { - cluster_domain = "${ var.cluster_domain }" - dns_service_ip = "${ var.dns_service_ip }" - kubelet_image_url = "${ var.kubelet_image_url }" - kubelet_image_tag = "${ var.kubelet_image_tag }" - internal_tld = "${ var.internal_tld }" - location = "${ var.location }" - cloud_config = "${ gzip_me.cloud_config }" - ca = "${ gzip_me.ca.output }" - k8s_worker = "${ gzip_me.k8s_worker.output }" - k8s_worker_key = "${ gzip_me.k8s_worker_key.output }" - } -} - resource "azurerm_network_interface" "cncf" { count = "${ var.worker_node_count }" name = "worker-interface${ count.index + 1 }" From ab58489c0d7c4d75856e22d977570d1b02da11f8 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 10:06:28 +1200 Subject: [PATCH 031/149] Fix typos to bring up AWS after refactor --- aws/aws.tf | 2 + aws/cert.tf | 8 ++++ aws/init-cfssl | 10 ++--- aws/modules.tf | 57 +++++++++++++--------------- aws/modules/etcd/cloud-config.tf | 4 -- aws/modules/kubeconfig/kubeconfig.tf | 2 +- aws/modules/vpc/input.tf | 4 -- aws/modules/vpc/vpc.tf | 1 - aws/modules/worker/cloud-config.tf | 4 -- aws/modules/worker/input.tf | 2 +- aws/modules/worker/output.tf | 2 +- 11 files changed, 44 insertions(+), 52 deletions(-) diff --git a/aws/aws.tf b/aws/aws.tf index a1d5306..89afffa 100644 --- a/aws/aws.tf +++ b/aws/aws.tf @@ -1,4 +1,6 @@ provider "aws" { } +provider "gzip" {compressionlevel = "BestCompression"} + # configured via: # $ export AWS_ACCESS_KEY_ID="anaccesskey" # $ export AWS_SECRET_ACCESS_KEY="asecretkey" diff --git a/aws/cert.tf b/aws/cert.tf index d87b469..0c3dd3a 100644 --- a/aws/cert.tf +++ b/aws/cert.tf @@ -8,6 +8,14 @@ ${ var.data_dir }/.cfssl \ ${ var.aws_region } \ ${ var.internal_tld } \ ${ var.k8s_service_ip } +EOF + } + + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = < Date: Mon, 17 Apr 2017 11:12:09 +1200 Subject: [PATCH 032/149] Bump Terraform Version from 0.9.0-beta2 to 0.9.3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2355f1e..b9b5c88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ENV KUBECTL_VERSION=v1.5.2 ENV GCLOUD_VERSION=150.0.0 ENV AWSCLI_VERSION=1.11.75 ENV AZURECLI_VERSION=2.0.2 -ENV TERRAFORM_VERSION=0.9.0-beta2 +ENV TERRAFORM_VERSION=0.9.3 ENV ARC=amd64 # Install AWS / AZURE CLI Deps From afbf8a7ad35299aa0cd2128cc3f0616d026ab735 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 11:14:49 +1200 Subject: [PATCH 033/149] Migrating from kubelet_aci to kubelet_image_url kubelet_aci and kubelet_version are now deprecated --- aws/input.tf | 4 ++-- aws/modules.tf | 8 ++++---- aws/modules/etcd/cloud-config.tf | 12 ++++++------ aws/modules/etcd/cloud-config.yml | 4 ++-- aws/modules/etcd/ec2.tf | 2 +- aws/modules/etcd/input.tf | 4 ++-- aws/modules/vpc/public.tf | 4 ++-- aws/modules/worker/cloud-config.tf | 4 ++-- aws/modules/worker/cloud-config.yml | 6 +++--- aws/modules/worker/ec2.tf | 2 +- aws/modules/worker/input.tf | 4 ++-- azure/modules/etcd/etcd-cloud-config.tf | 10 +++++----- azure/readme.org | 2 +- 13 files changed, 33 insertions(+), 33 deletions(-) diff --git a/aws/input.tf b/aws/input.tf index c8b5996..f79615f 100644 --- a/aws/input.tf +++ b/aws/input.tf @@ -20,7 +20,7 @@ variable "aws_worker_vm_size" { default = "m3.medium" } variable "aws_bastion_vm_size" { default = "t2.nano" } # Set from https://quay.io/repository/coreos/hyperkube?tab=tags -variable "kubelet_aci" { default = "quay.io/coreos/hyperkube"} -variable "kubelet_version" { default = "v1.5.1_coreos.0"} +variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} +variable "kubelet_image_tag" { default = "v1.5.1_coreos.0"} variable "data_dir" { default = "/cncf/data/aws" } diff --git a/aws/modules.tf b/aws/modules.tf index b0bd8f7..0ef24b1 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -40,8 +40,8 @@ module "etcd" { ami_id = "${ var.aws_image_ami }" key_name = "${ var.aws_key_name }" cluster_domain = "${ var.cluster_domain }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" dns_service_ip = "${ var.dns_service_ip }" etcd_ips = "${ var.etcd_ips }" etcd_security_group_id = "${ module.security.etcd_id }" @@ -87,8 +87,8 @@ module "worker" { min = 3 } cluster_domain = "${ var.cluster_domain }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" dns_service_ip = "${ var.dns_service_ip }" instance_type = "${ var.aws_worker_vm_size }" internal_tld = "${ var.internal_tld }" diff --git a/aws/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf index 87e8d4a..68d39ff 100644 --- a/aws/modules/etcd/cloud-config.tf +++ b/aws/modules/etcd/cloud-config.tf @@ -24,9 +24,9 @@ data "template_file" "kube-apiserver" { vars { internal_tld = "${ var.internal_tld }" service_cidr = "${ var.service_cidr }" - hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" + hyperkube = "${ var.kubelet_image_url }:${ var.kubelet_image_tag }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" } } @@ -44,9 +44,9 @@ data "template_file" "cloud-config" { dns_service_ip = "${ var.dns_service_ip }" fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" hostname = "etcd${ count.index + 1 }" - hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" + hyperkube = "${ var.kubelet_image_url }:${ var.kubelet_image_tag }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" internal_tld = "${ var.internal_tld }" pod_cidr = "${ var.pod_cidr }" region = "${ var.region }" diff --git a/aws/modules/etcd/cloud-config.yml b/aws/modules/etcd/cloud-config.yml index b8da339..27524c2 100644 --- a/aws/modules/etcd/cloud-config.yml +++ b/aws/modules/etcd/cloud-config.yml @@ -57,8 +57,8 @@ coreos: [Unit] ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper [Service] - Environment="KUBELET_ACI=${ kubelet_aci }" - Environment="KUBELET_VERSION=${ kubelet_version }" + Environment="KUBELET_IMAGE_URL=${ kubelet_image_url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet_image_tag }" Environment="RKT_OPTS=\ --volume dns,kind=host,source=/etc/resolv.conf \ --mount volume=dns,target=/etc/resolv.conf \ diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 935f34e..78e1e68 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -23,7 +23,7 @@ resource "aws_instance" "etcd" { kz8s = "${ var.name }" Name = "etcd${ count.index + 1 }-${ var.name }" role = "etcd,apiserver" - version = "${ var.kubelet_version }" + version = "${ var.kubelet_image_tag }" visibility = "private" } diff --git a/aws/modules/etcd/input.tf b/aws/modules/etcd/input.tf index e0ca731..e3edb91 100644 --- a/aws/modules/etcd/input.tf +++ b/aws/modules/etcd/input.tf @@ -1,7 +1,7 @@ variable "ami_id" {} variable "cluster_domain" {} -variable "kubelet_aci" {} -variable "kubelet_version" {} +variable "kubelet_image_url" {} +variable "kubelet_image_tag" {} variable "depends_id" {} variable "dns_service_ip" {} variable "etcd_ips" {} diff --git a/aws/modules/vpc/public.tf b/aws/modules/vpc/public.tf index 964d8e3..5d7b1af 100644 --- a/aws/modules/vpc/public.tf +++ b/aws/modules/vpc/public.tf @@ -6,7 +6,7 @@ resource "aws_internet_gateway" "main" { KubernetesCluster = "${ var.name }" kz8s = "${ var.name }" Name = "kz8s-${ var.name }" - version = "${ var.kubelet_version }" + version = "${ var.kubelet_image_tag }" } } @@ -23,7 +23,7 @@ resource "aws_subnet" "public" { KubernetesCluster = "${ var.name }" kz8s = "${ var.name }" Name = "kz8s-${ var.name }-public" - version = "${ var.kubelet_version }" + version = "${ var.kubelet_image_tag }" visibility = "public" } } diff --git a/aws/modules/worker/cloud-config.tf b/aws/modules/worker/cloud-config.tf index 4f74ea6..ff8c76e 100644 --- a/aws/modules/worker/cloud-config.tf +++ b/aws/modules/worker/cloud-config.tf @@ -16,8 +16,8 @@ data "template_file" "cloud-config" { vars { cluster_domain = "${ var.cluster_domain }" dns_service_ip = "${ var.dns_service_ip }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" internal_tld = "${ var.internal_tld }" region = "${ var.region }" ca = "${ gzip_me.ca.output }" diff --git a/aws/modules/worker/cloud-config.yml b/aws/modules/worker/cloud-config.yml index e91402f..786a7c6 100644 --- a/aws/modules/worker/cloud-config.yml +++ b/aws/modules/worker/cloud-config.yml @@ -68,8 +68,8 @@ coreos: [Unit] ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper [Service] - Environment="KUBELET_ACI=${ kubelet_aci }" - Environment="KUBELET_VERSION=${ kubelet_version }" + Environment="KUBELET_IMAGE_URL=${ kubelet_image_url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet_image_tag }" Environment="RKT_OPTS=\ --volume dns,kind=host,source=/etc/resolv.conf \ --mount volume=dns,target=/etc/resolv.conf \ @@ -143,7 +143,7 @@ write-files: hostNetwork: true containers: - name: kube-proxy - image: ${ kubelet_aci }:${ kubelet_version } + image: ${ kubelet_image_url }:${ kubelet_image_tag } command: - /hyperkube - proxy diff --git a/aws/modules/worker/ec2.tf b/aws/modules/worker/ec2.tf index a03a46d..838ba38 100644 --- a/aws/modules/worker/ec2.tf +++ b/aws/modules/worker/ec2.tf @@ -77,7 +77,7 @@ resource "aws_autoscaling_group" "worker" { tag { key = "version" - value = "${ var.kubelet_version }" + value = "${ var.kubelet_image_tag }" propagate_at_launch = true } diff --git a/aws/modules/worker/input.tf b/aws/modules/worker/input.tf index 2ce4d23..ea658ba 100644 --- a/aws/modules/worker/input.tf +++ b/aws/modules/worker/input.tf @@ -7,8 +7,8 @@ variable "capacity" { } } variable "cluster_domain" {} -variable "kubelet_aci" {} -variable "kubelet_version" {} +variable "kubelet_image_url" {} +variable "kubelet_image_tag" {} variable "depends_id" {} variable "dns_service_ip" {} variable "instance_type" {} diff --git a/azure/modules/etcd/etcd-cloud-config.tf b/azure/modules/etcd/etcd-cloud-config.tf index 2bfbc20..9ef91da 100644 --- a/azure/modules/etcd/etcd-cloud-config.tf +++ b/azure/modules/etcd/etcd-cloud-config.tf @@ -29,18 +29,18 @@ resource "gzip_me" "k8s_apiserver_key" { input = "${ var.k8s_apiserver_key }" } -data "template_file" "kube-apiserver" { +data "template_file" "kube_apiserver" { template = "${ file( "${ path.module }/kube-apiserver.yml" )}" vars { internal_tld = "${ var.internal_tld }" service_cidr = "${ var.service_cidr }" - hyperkube = "${ var.kubelet_aci }:${ var.kubelet_version }" - kubelet_aci = "${ var.kubelet_aci }" - kubelet_version = "${ var.kubelet_version }" + hyperkube = "${ var.kubelet_image_url }:${ var.kubelet_image_tag }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" } } -data "template_file" "etcd-cloud-config" { +data "template_file" "etcd_cloud_config" { count = "${ var.master_node_count }" template = "${ file( "${ path.module }/etcd-cloud-config.yml" )}" diff --git a/azure/readme.org b/azure/readme.org index 00f623e..3d78322 100644 --- a/azure/readme.org +++ b/azure/readme.org @@ -67,7 +67,7 @@ worker_node_count= "3" master_vm_size= "Standard_A2" worker_vm_size= "Standard_A2" bastion_vm_size= "Standard_A2" -kubelet_image_url= "quay.io/coreos/hyperkube" +kubelet_aci= "quay.io/coreos/hyperkube" kubelet_image_tag= "v1.4.7_coreos.0" image_publisher= "CoreOS" image_offer= "CoreOS" From c3605e1eba722b2eac4d7e1ee680a78854cd244f Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 11:18:01 +1200 Subject: [PATCH 034/149] Rename vpc to network -Azure uses virtual_networks --- azure/modules.tf | 15 +++++++++------ azure/modules/{vpc => network}/input.tf | 0 azure/modules/{vpc => network}/output.tf | 0 azure/modules/{vpc => network}/virtual_network.tf | 2 ++ 4 files changed, 11 insertions(+), 6 deletions(-) rename azure/modules/{vpc => network}/input.tf (100%) rename azure/modules/{vpc => network}/output.tf (100%) rename azure/modules/{vpc => network}/virtual_network.tf (94%) diff --git a/azure/modules.tf b/azure/modules.tf index c5f8240..593235c 100644 --- a/azure/modules.tf +++ b/azure/modules.tf @@ -1,5 +1,5 @@ -module "vpc" { - source = "./modules/vpc" +module "network" { + source = "./modules/network" name = "${ var.name }" cidr = "${ var.vpc_cidr }" name_servers_file = "${ module.dns.name_servers_file }" @@ -24,10 +24,11 @@ module "etcd" { image_offer = "${ var.image_offer }" image_sku = "${ var.image_sku }" image_version = "${ var.image_version }" - subnet_id = "${ module.vpc.subnet_id }" + subnet_id = "${ module.network.subnet_id }" storage_account = "${ azurerm_storage_account.cncf.name }" storage_primary_endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" - storage_container = "${ azurerm_storage_container.cncf.name }" + storage_container = "${ var.name }" + # storage_container = "${ azurerm_storage_container.cncf.name }" availability_id = "${ azurerm_availability_set.cncf.id }" cluster_domain = "${ var.cluster_domain }" kubelet_image_url = "${ var.kubelet_image_url }" @@ -42,6 +43,7 @@ module "etcd" { k8s_etcd_key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" k8s_apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" k8s_apiserver_key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" + data_dir = "${ var.data_dir }" } @@ -55,11 +57,12 @@ module "bastion" { image_sku = "${ var.image_sku }" image_version = "${ var.image_version }" admin_username = "${ var.admin_username }" - subnet_id = "${ module.vpc.subnet_id }" + subnet_id = "${ module.network.subnet_id }" storage_primary_endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" storage_container = "${ azurerm_storage_container.cncf.name }" availability_id = "${ azurerm_availability_set.cncf.id }" internal_tld = "${ var.internal_tld }" + data_dir = "${ var.data_dir }" } module "worker" { @@ -73,7 +76,7 @@ module "worker" { image_offer = "${ var.image_offer }" image_sku = "${ var.image_sku }" image_version = "${ var.image_version }" - subnet_id = "${ module.vpc.subnet_id }" + subnet_id = "${ module.network.subnet_id }" storage_account = "${ azurerm_storage_account.cncf.name }" storage_primary_endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" storage_container = "${ azurerm_storage_container.cncf.name }" diff --git a/azure/modules/vpc/input.tf b/azure/modules/network/input.tf similarity index 100% rename from azure/modules/vpc/input.tf rename to azure/modules/network/input.tf diff --git a/azure/modules/vpc/output.tf b/azure/modules/network/output.tf similarity index 100% rename from azure/modules/vpc/output.tf rename to azure/modules/network/output.tf diff --git a/azure/modules/vpc/virtual_network.tf b/azure/modules/network/virtual_network.tf similarity index 94% rename from azure/modules/vpc/virtual_network.tf rename to azure/modules/network/virtual_network.tf index 6f70327..1ea0582 100644 --- a/azure/modules/vpc/virtual_network.tf +++ b/azure/modules/network/virtual_network.tf @@ -1,5 +1,7 @@ resource "azurerm_network_security_group" "cncf" { name = "${ var.name }" + location = "${ var.location}" + resource_group_name = "${ var.name }" } resource "azurerm_subnet" "cncf" { From 846f408360e12b6ca58564c363b95806c9859031 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 11:20:04 +1200 Subject: [PATCH 035/149] Clean up Azure API server to match AWS --- azure/modules.tf | 4 ++++ azure/modules/bastion/bastion-node.tf | 2 +- azure/modules/bastion/input.tf | 1 + azure/modules/bastion/output.tf | 2 +- azure/modules/etcd/etcd-cloud-config.tf | 8 ++++---- azure/modules/etcd/etcd-cloud-config.yml | 2 +- azure/modules/etcd/etcd-load-balancer.tf | 2 +- azure/modules/etcd/etcd-nodes.tf | 2 +- azure/modules/etcd/input.tf | 4 ++-- azure/modules/etcd/{api-server.yml => kube-apiserver.yml} | 1 - azure/modules/worker/input.tf | 5 +++-- azure/modules/worker/worker-cloud-config.tf | 8 ++++---- azure/modules/worker/worker-cloud-config.yml | 2 +- azure/modules/worker/worker-nodes.tf | 2 +- 14 files changed, 25 insertions(+), 20 deletions(-) rename azure/modules/etcd/{api-server.yml => kube-apiserver.yml} (99%) diff --git a/azure/modules.tf b/azure/modules.tf index 593235c..04f3d2b 100644 --- a/azure/modules.tf +++ b/azure/modules.tf @@ -88,6 +88,10 @@ module "worker" { dns_service_ip = "${ var.dns_service_ip }" internal_tld = "${ var.internal_tld }" k8s_cloud_config = "${file("${ var.data_dir }/azure-config.json")}" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s_worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" + k8s_worker_key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" + data_dir = "${ var.data_dir }" } diff --git a/azure/modules/bastion/bastion-node.tf b/azure/modules/bastion/bastion-node.tf index b764a43..d589c2e 100644 --- a/azure/modules/bastion/bastion-node.tf +++ b/azure/modules/bastion/bastion-node.tf @@ -53,7 +53,7 @@ resource "azurerm_virtual_machine" "cncf" { disable_password_authentication = true ssh_keys { path = "/home/${ var.admin_username }/.ssh/authorized_keys" - key_data = "${file("${vardata_dir}.ssh/id_rsa.pub")}" + key_data = "${file("${ var.data_dir }/.ssh/id_rsa.pub")}" } } } diff --git a/azure/modules/bastion/input.tf b/azure/modules/bastion/input.tf index 85c44a7..9dc1a42 100644 --- a/azure/modules/bastion/input.tf +++ b/azure/modules/bastion/input.tf @@ -11,6 +11,7 @@ variable "subnet_id" {} variable "availability_id" {} variable "storage_container" {} variable "storage_primary_endpoint" {} +variable "data_dir" {} # variable "allow_ssh_cidr" {} # variable "security_group_id" {} diff --git a/azure/modules/bastion/output.tf b/azure/modules/bastion/output.tf index 88c4455..2f8f327 100644 --- a/azure/modules/bastion/output.tf +++ b/azure/modules/bastion/output.tf @@ -1,2 +1,2 @@ output "bastion_ip" { value = "${azurerm_public_ip.cncf.ip_address}" } -output "bastion-fqdn" { value = "${azurerm_public_ip.cncf.fqdn}" } +output "bastion_fqdn" { value = "${azurerm_public_ip.cncf.fqdn}" } diff --git a/azure/modules/etcd/etcd-cloud-config.tf b/azure/modules/etcd/etcd-cloud-config.tf index 9ef91da..e73acdd 100644 --- a/azure/modules/etcd/etcd-cloud-config.tf +++ b/azure/modules/etcd/etcd-cloud-config.tf @@ -3,10 +3,10 @@ provider "gzip" { } resource "gzip_me" "kube-apiserver" { - input = "${ data.template_file.kube-apiserver.rendered }" + input = "${ data.template_file.kube_apiserver.rendered }" } -resource "gzip_me" "cloud_config" { - input = "${ var.cloud_config }" +resource "gzip_me" "k8s_cloud_config" { + input = "${ var.k8s_cloud_config }" } resource "gzip_me" "ca" { @@ -57,7 +57,7 @@ data "template_file" "etcd_cloud_config" { pod_cidr = "${ var.pod_cidr }" location = "${ var.location }" service_cidr = "${ var.service_cidr }" - cloud_config = "${ gzip_me.cloud_config }" + k8s_cloud_config = "${ gzip_me.k8s_cloud_config.output }" ca = "${ gzip_me.ca.output }" k8s_etcd = "${ gzip_me.k8s_etcd.output }" k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" diff --git a/azure/modules/etcd/etcd-cloud-config.yml b/azure/modules/etcd/etcd-cloud-config.yml index 7bfc561..e881df5 100644 --- a/azure/modules/etcd/etcd-cloud-config.yml +++ b/azure/modules/etcd/etcd-cloud-config.yml @@ -230,4 +230,4 @@ write-files: permissions: "0644" encoding: "gzip+base64" content: | - ${ cloud_config } + ${ k8s_cloud_config } diff --git a/azure/modules/etcd/etcd-load-balancer.tf b/azure/modules/etcd/etcd-load-balancer.tf index 1266605..e0fad61 100644 --- a/azure/modules/etcd/etcd-load-balancer.tf +++ b/azure/modules/etcd/etcd-load-balancer.tf @@ -3,7 +3,7 @@ resource "azurerm_public_ip" "cncf" { location = "${ var.location }" resource_group_name = "${ var.name }" public_ip_address_allocation = "static" - domain_name_label = "k8s${ var.name }" + domain_name_label = "k8s-${ var.name }" } resource "azurerm_lb" "cncf" { diff --git a/azure/modules/etcd/etcd-nodes.tf b/azure/modules/etcd/etcd-nodes.tf index 39d19ce..1bb779c 100644 --- a/azure/modules/etcd/etcd-nodes.tf +++ b/azure/modules/etcd/etcd-nodes.tf @@ -40,7 +40,7 @@ resource "azurerm_virtual_machine" "cncf" { computer_name = "etcd-master${ count.index + 1 }" admin_username = "${ var.admin_username }" admin_password = "Password1234!" - custom_data = "${ element(data.template_file.cloud_config.*.rendered, count.index) }" + custom_data = "${ element(data.template_file.etcd_cloud_config.*.rendered, count.index) }" } os_profile_linux_config { diff --git a/azure/modules/etcd/input.tf b/azure/modules/etcd/input.tf index da72558..a7a2bb6 100644 --- a/azure/modules/etcd/input.tf +++ b/azure/modules/etcd/input.tf @@ -11,7 +11,6 @@ variable "availability_id" {} variable "storage_account" {} variable "storage_primary_endpoint" {} variable "storage_container" {} -variable "k8s-apiserver-tar" {} variable "cluster_domain" {} variable "dns_service_ip" {} variable "internal_tld" {} @@ -20,7 +19,7 @@ variable "service_cidr" {} variable "admin_username" {} variable "kubelet_image_url" {} variable "kubelet_image_tag" {} -variable "cloud_config" {} +variable "k8s_cloud_config" {} # variable "etcd_security_group_id" {} # variable "external_elb_security_group_id" {} variable "ca" {} @@ -28,3 +27,4 @@ variable "k8s_etcd" {} variable "k8s_etcd_key" {} variable "k8s_apiserver" {} variable "k8s_apiserver_key" {} +variable "data_dir" {} diff --git a/azure/modules/etcd/api-server.yml b/azure/modules/etcd/kube-apiserver.yml similarity index 99% rename from azure/modules/etcd/api-server.yml rename to azure/modules/etcd/kube-apiserver.yml index ba9f77a..8e9e116 100644 --- a/azure/modules/etcd/api-server.yml +++ b/azure/modules/etcd/kube-apiserver.yml @@ -1,4 +1,3 @@ --- apiVersion: v1 kind: Pod metadata: diff --git a/azure/modules/worker/input.tf b/azure/modules/worker/input.tf index 3f5df91..aa275e2 100644 --- a/azure/modules/worker/input.tf +++ b/azure/modules/worker/input.tf @@ -11,14 +11,15 @@ variable "storage_account" {} variable "storage_primary_endpoint" {} variable "storage_container" {} variable "availability_id" {} -variable "external-lb" {} +variable "external_lb" {} variable "cluster_domain" {} variable "dns_service_ip" {} variable "internal_tld" {} variable "admin_username" {} variable "kubelet_image_url" {} variable "kubelet_image_tag" {} -variable "cloud_config" {} +variable "k8s_cloud_config" {} variable "ca" {} variable "k8s_worker" {} variable "k8s_worker_key" {} +variable "data_dir" {} diff --git a/azure/modules/worker/worker-cloud-config.tf b/azure/modules/worker/worker-cloud-config.tf index ee8c2e1..79a589a 100644 --- a/azure/modules/worker/worker-cloud-config.tf +++ b/azure/modules/worker/worker-cloud-config.tf @@ -2,8 +2,8 @@ provider "gzip" { compressionlevel = "BestCompression" } -resource "gzip_me" "cloud_config" { - input = "${ var.cloud_config }" +resource "gzip_me" "k8s_cloud_config" { + input = "${ var.k8s_cloud_config }" } resource "gzip_me" "ca" { @@ -18,7 +18,7 @@ resource "gzip_me" "k8s_worker_key" { input = "${ var.k8s_worker_key }" } -data "template_file" "worker-cloud-config" { +data "template_file" "worker_cloud_config" { template = "${ file( "${ path.module }/worker-cloud-config.yml" )}" vars { @@ -28,7 +28,7 @@ data "template_file" "worker-cloud-config" { kubelet_image_tag = "${ var.kubelet_image_tag }" internal_tld = "${ var.internal_tld }" location = "${ var.location }" - cloud_config = "${ gzip_me.cloud_config }" + k8s_cloud_config = "${ gzip_me.k8s_cloud_config.output }" ca = "${ gzip_me.ca.output }" k8s_worker = "${ gzip_me.k8s_worker.output }" k8s_worker_key = "${ gzip_me.k8s_worker_key.output }" diff --git a/azure/modules/worker/worker-cloud-config.yml b/azure/modules/worker/worker-cloud-config.yml index 9c605a2..e739bb4 100644 --- a/azure/modules/worker/worker-cloud-config.yml +++ b/azure/modules/worker/worker-cloud-config.yml @@ -162,4 +162,4 @@ write-files: permissions: "0644" encoding: "gzip+base64" content: | - ${ cloud_config } + ${ k8s_cloud_config } diff --git a/azure/modules/worker/worker-nodes.tf b/azure/modules/worker/worker-nodes.tf index d83c00c..7b28b44 100644 --- a/azure/modules/worker/worker-nodes.tf +++ b/azure/modules/worker/worker-nodes.tf @@ -38,7 +38,7 @@ resource "azurerm_virtual_machine" "cncf" { computer_name = "worker-node${ count.index + 1 }" admin_username = "${ var.admin_username }" admin_password = "Password1234!" - custom_data = "${ element(data.template_file.cloud_config.*.rendered, count.index) }" + custom_data = "${ element(data.template_file.worker_cloud_config.*.rendered, count.index) }" } os_profile_linux_config { From b0349c2362fd62a1293ba0f4c2db01b9c49450cd Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 17 Apr 2017 11:21:16 +1200 Subject: [PATCH 036/149] Refactor entrypoint to support aws+azure --- entrypoint.sh | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index cc366f7..eab357c 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -12,31 +12,35 @@ providers { gzip = "terraform-provider-gzip" } EOF - fi } export TF_VAR_name="$2" +export TF_VAR_internal_tld=${TF_VAR_name}.cncf.demo +export TF_VAR_data_dir=/cncf/data/${TF_VAR_name} # Run CMD if [ "$1" = "aws-deploy" ] ; then write_terraformrc - terraform get /deploy/aws && \ - terraform apply -target null_resource.ssl_gen /deploy/aws && \ + terraform get /build/aws && \ + terraform apply -target null_resource.ssl_gen /build/aws && \ time terraform apply /deploy/aws && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/ \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/kubeconfig \n\n'${NC} elif [ "$1" = "aws-destroy" ] ; then write_aws_config - time terraform destroy -force /deploy/aws + time terraform destroy -force /build/aws elif [ "$1" = "azure-deploy" ] ; then - terraform get /azure && \ - terraform apply -target null_resource.sshkey_gen /deploy/azure && \ - terraform apply -target null_resource.ssl_gen /deploy/azure && \ - terraform apply -target null_resource.cloud_gen /deploy/azure && \ - terraform apply -target module.dns.null_resource.dns_gen /delpoy/azure && \ - terraform apply -target module.etcd.azurerm_network_interface.cncf /delpoy/azure && \ - time terraform apply /deploy/azure && \ + # There are some dependency issues around cert,sshkey,k8s_cloud_config, and dns + # since they use files on disk that are created on the fly + # should probably move these to data resources + terraform get /build/azure && \ + terraform apply -target null_resource.sshkey_gen /build/azure && \ + terraform apply -target null_resource.ssl_gen /build/azure && \ + terraform apply -target null_resource.cloud_gen /build/azure && \ + terraform apply -target module.dns.null_resource.dns_gen /build/azure && \ + terraform apply -target module.etcd.azurerm_network_interface.cncf /build/azure && \ + time terraform apply /build/azure && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} From ba3d44ec937b1dfa5abfb894b42111c1d8c0c4b4 Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 17 Apr 2017 18:34:32 +1200 Subject: [PATCH 037/149] Fix Missing Content for write_files kube-apiserver.yml --- azure/modules/etcd/etcd-cloud-config.tf | 2 +- azure/modules/etcd/etcd-cloud-config.yml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/azure/modules/etcd/etcd-cloud-config.tf b/azure/modules/etcd/etcd-cloud-config.tf index e73acdd..da52e0d 100644 --- a/azure/modules/etcd/etcd-cloud-config.tf +++ b/azure/modules/etcd/etcd-cloud-config.tf @@ -63,7 +63,7 @@ data "template_file" "etcd_cloud_config" { k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" k8s_apiserver = "${ gzip_me.k8s_apiserver.output }" k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" - k8s-apiserver-yml = "${ gzip_me.kube-apiserver.output }" + k8s_apiserver_yml = "${ gzip_me.kube-apiserver.output }" node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" } diff --git a/azure/modules/etcd/etcd-cloud-config.yml b/azure/modules/etcd/etcd-cloud-config.yml index e881df5..5fe6e0a 100644 --- a/azure/modules/etcd/etcd-cloud-config.yml +++ b/azure/modules/etcd/etcd-cloud-config.yml @@ -81,7 +81,10 @@ write-files: exec nsenter -m -u -i -n -p -t 1 -- /usr/bin/rkt "$@" - path: /etc/kubernetes/manifests/kube-apiserver.yml + encoding: "gzip+base64" content: | + ${ k8s_apiserver_yml } + - path: /etc/kubernetes/manifests/kube-controller-manager.yml content: | apiVersion: v1 From 1e62a3988d45dd78d917955487c6f28e5348bac5 Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 17 Apr 2017 18:35:51 +1200 Subject: [PATCH 038/149] Fix YAML Syntax Formatting --- azure/modules/etcd/kube-apiserver.yml | 102 +++++++++++++------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/azure/modules/etcd/kube-apiserver.yml b/azure/modules/etcd/kube-apiserver.yml index 8e9e116..c85a1f5 100644 --- a/azure/modules/etcd/kube-apiserver.yml +++ b/azure/modules/etcd/kube-apiserver.yml @@ -6,54 +6,54 @@ metadata: spec: hostNetwork: true containers: - - name: kube-apiserver - image: ${ kubelet_image_url }:${ kubelet_image_tag } - command: - - /hyperkube - - apiserver - - --admission-control=LimitRanger - - --admission-control=NamespaceExists - - --admission-control=NamespaceLifecycle - - --admission-control=ResourceQuota - - --admission-control=SecurityContextDeny - - --admission-control=ServiceAccount - - --allow-privileged=true - - --client-ca-file=/etc/kubernetes/ssl/ca.pem - - --cloud-provider=azure - - --cloud-config=/etc/kubernetes/ssl/azure-config.json - - --etcd-servers=http://etcd.${ internal_tld }:2379 - - --insecure-bind-address=0.0.0.0 - - --secure-port=443 - - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --service-cluster-ip-range=${ service_cidr } - - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem - - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - - --v=2 - livenessProbe: - httpGet: - host: 127.0.0.1 - port: 8080 - path: /healthz - initialDelaySeconds: 15 - timeoutSeconds: 15 - ports: - - containerPort: 443 - hostPort: 443 - name: https - - containerPort: 8080 - hostPort: 8080 - name: local - volumeMounts: - - mountPath: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - readOnly: true - - mountPath: /etc/ssl/certs - name: ssl-certs-host - readOnly: true - volumes: - - hostPath: - path: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - - hostPath: - path: /usr/share/ca-certificates - name: ssl-certs-host + - name: kube-apiserver + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - apiserver + - --admission-control=LimitRanger + - --admission-control=NamespaceExists + - --admission-control=NamespaceLifecycle + - --admission-control=ResourceQuota + - --admission-control=SecurityContextDeny + - --admission-control=ServiceAccount + - --allow-privileged=true + - --client-ca-file=/etc/kubernetes/ssl/ca.pem + - --cloud-provider=azure + - --cloud-config=/etc/kubernetes/ssl/azure-config.json + - --etcd-servers=http://etcd.${ internal_tld }:2379 + - --insecure-bind-address=0.0.0.0 + - --secure-port=443 + - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --service-cluster-ip-range=${ service_cidr } + - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem + - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --v=2 + livenessProbe: + httpGet: + host: 127.0.0.1 + port: 8080 + path: /healthz + initialDelaySeconds: 15 + timeoutSeconds: 15 + ports: + - containerPort: 443 + hostPort: 443 + name: https + - containerPort: 8080 + hostPort: 8080 + name: local + volumeMounts: + - mountPath: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + readOnly: true + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host From 32b9e76148512fc932aa9abe337c3d05bbc82da0 Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 17 Apr 2017 18:42:38 +1200 Subject: [PATCH 039/149] Merge cert.cf & keypair.tf so only one -target apply is needed --- azure/cert.tf | 27 --------------------------- azure/keypair.tf | 22 ---------------------- azure/ssl-ssh.tf | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 49 deletions(-) delete mode 100644 azure/cert.tf delete mode 100644 azure/keypair.tf create mode 100644 azure/ssl-ssh.tf diff --git a/azure/cert.tf b/azure/cert.tf deleted file mode 100644 index 16835e2..0000000 --- a/azure/cert.tf +++ /dev/null @@ -1,27 +0,0 @@ -#Gen Certs -resource "null_resource" "ssl_gen" { - - provisioner "local-exec" { - command = < Date: Mon, 17 Apr 2017 18:48:27 +1200 Subject: [PATCH 040/149] Merge cloud-config.tf into ssl-ssh-cloud.tf --- azure/cloud-config.tf | 37 -------------------------- azure/{ssl-ssh.tf => ssl-ssh-cloud.tf} | 0 2 files changed, 37 deletions(-) delete mode 100644 azure/cloud-config.tf rename azure/{ssl-ssh.tf => ssl-ssh-cloud.tf} (100%) diff --git a/azure/cloud-config.tf b/azure/cloud-config.tf deleted file mode 100644 index 0a040f7..0000000 --- a/azure/cloud-config.tf +++ /dev/null @@ -1,37 +0,0 @@ -#Gen Certs -resource "null_resource" "cloud_gen" { - - provisioner "local-exec" { - command = < ${ var.data_dir }/azure-config.json -{ - "aadClientId": "$${ARM_CLIENT_ID}", - "aadClientSecret": "$${ARM_CLIENT_SECRET}", - "tenantId": "$${ARM_TENANT_ID}", - "subscriptionId": "$${ARM_SUBSCRIPTION_ID}", - "resourceGroup": "${ var.name }", - "location": "${ var.location }", - "subnetName": "${ var.name }", - "securityGroupName": "${ var.name }", - "vnetName": "${ var.name }", - "routeTableName": "${ var.name }", - "primaryAvailabilitySetName": "${ var.name }" -} -JSON -COMMAND - } - - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = < Date: Mon, 17 Apr 2017 18:59:34 +1200 Subject: [PATCH 041/149] Add Prereqs to a Single Resource so -target is only needed once --- azure/ssl-ssh-cloud.tf | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/azure/ssl-ssh-cloud.tf b/azure/ssl-ssh-cloud.tf index cb8432b..d02a3a0 100644 --- a/azure/ssl-ssh-cloud.tf +++ b/azure/ssl-ssh-cloud.tf @@ -1,5 +1,5 @@ #Gen Certs and SSH KeyPair -resource "null_resource" "ssl_ssh_gen" { +resource "null_resource" "ssl_ssh_cloud_gen" { provisioner "local-exec" { command = < ${ var.data_dir }/azure-config.json +{ + "aadClientId": "$${ARM_CLIENT_ID}", + "aadClientSecret": "$${ARM_CLIENT_SECRET}", + "tenantId": "$${ARM_TENANT_ID}", + "subscriptionId": "$${ARM_SUBSCRIPTION_ID}", + "resourceGroup": "${ var.name }", + "location": "${ var.location }", + "subnetName": "${ var.name }", + "securityGroupName": "${ var.name }", + "vnetName": "${ var.name }", + "routeTableName": "${ var.name }", + "primaryAvailabilitySetName": "${ var.name }" +} +JSON +COMMAND + } + + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = < Date: Mon, 17 Apr 2017 19:35:59 +1200 Subject: [PATCH 042/149] Remove DNS Count Depends of Master Node IPs so terraform apply can be run without computing interface values first --- azure/modules.tf | 1 + azure/modules/dns/dns.tf | 4 ++-- azure/modules/dns/input.tf | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/azure/modules.tf b/azure/modules.tf index 04f3d2b..31ff773 100644 --- a/azure/modules.tf +++ b/azure/modules.tf @@ -11,6 +11,7 @@ module "dns" { name = "${ var.name }" internal_tld = "${ var.internal_tld }" master_ips = "${ module.etcd.master_ips }" + master_node_count = "${ var.master_node_count }" } module "etcd" { diff --git a/azure/modules/dns/dns.tf b/azure/modules/dns/dns.tf index 1bf3e09..28206e3 100644 --- a/azure/modules/dns/dns.tf +++ b/azure/modules/dns/dns.tf @@ -47,7 +47,7 @@ resource "azurerm_dns_a_record" "A-etcd" { } resource "azurerm_dns_a_record" "A-etcds" { - count = "${ length(var.master_ips) }" + count = "${ var.master_node_count }" name = "etcd${ count.index+1 }" zone_name = "${azurerm_dns_zone.cncf.name}" @@ -67,7 +67,7 @@ resource "azurerm_dns_a_record" "A-master" { } resource "azurerm_dns_a_record" "A-masters" { - count = "${ length(var.master_ips) }" + count = "${ var.master_node_count }" name = "master${ count.index+1 }" zone_name = "${azurerm_dns_zone.cncf.name}" resource_group_name = "${ var.name }" diff --git a/azure/modules/dns/input.tf b/azure/modules/dns/input.tf index f29b598..8f1d770 100644 --- a/azure/modules/dns/input.tf +++ b/azure/modules/dns/input.tf @@ -1,6 +1,5 @@ -# variable "depends_id" {} variable "internal_tld" {} variable "name" {} variable "name_servers_file" { default = "azure_dns_zone"} variable "master_ips" { type = "list" } -# variable "vpc_id" {} +variable "master_node_count" {} From f614cfc4d5d5d6b4af9f3873d2e6d7543ac7880a Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 17 Apr 2017 19:38:51 +1200 Subject: [PATCH 043/149] Write Kubeconfig to Deploy DIR --- azure/modules/kubeconfig/kubeconfig.tf | 35 +++----------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/azure/modules/kubeconfig/kubeconfig.tf b/azure/modules/kubeconfig/kubeconfig.tf index 445c51c..24f41b9 100644 --- a/azure/modules/kubeconfig/kubeconfig.tf +++ b/azure/modules/kubeconfig/kubeconfig.tf @@ -1,48 +1,21 @@ -data "template_file" "kubeconfig" { - template = < ${ var.data_dir }/kubeconfig -${data.template_file.kubeconfig.rendered} -__USERDATA__ -LOCAL_EXEC - } - - provisioner "local-exec" { - command = < Date: Tue, 18 Apr 2017 05:46:40 +1200 Subject: [PATCH 044/149] standardize input vars for azure+aws --- aws/input.tf | 28 ++++++++++------ aws/modules.tf | 17 +++++----- .../{route53/route53.tf => dns/dns.tf} | 9 ++---- aws/modules/dns/input.tf | 6 ++++ aws/modules/{route53/io.tf => dns/output.tf} | 5 --- aws/modules/etcd/cloud-config.tf | 2 +- aws/modules/etcd/ec2.tf | 3 +- aws/modules/etcd/input.tf | 3 +- aws/modules/etcd/output.tf | 2 ++ azure/input.tf | 32 ++++++++++++------- 10 files changed, 61 insertions(+), 46 deletions(-) rename aws/modules/{route53/route53.tf => dns/dns.tf} (88%) create mode 100644 aws/modules/dns/input.tf rename aws/modules/{route53/io.tf => dns/output.tf} (72%) diff --git a/aws/input.tf b/aws/input.tf index f79615f..0983dd9 100644 --- a/aws/input.tf +++ b/aws/input.tf @@ -1,26 +1,36 @@ variable "name" { default = "aws" } + +variable "internal_tld" { default = "aws.cncf.demo" } +variable "data_dir" { default = "/cncf/data/aws" } + +# AWS Cloud Specific Settings variable "aws_region" { default = "ap-southeast-2" } variable "aws_key_name" { default = "aws" } variable "aws_azs" { default = "ap-southeast-2a,ap-southeast-2b,ap-southeast-2c" } - -variable "internal_tld" { default = "aws.cncf.demo" } -variable "cluster_domain" { default = "cluster.local" } -variable "etcd_ips" { default = "10.0.10.10,10.0.10.11,10.0.10.12" } variable "vpc_cidr" { default = "10.0.0.0/16" } -variable "pod_cidr" { default = "10.2.0.0/16" } -variable "service_cidr" { default = "10.3.0.0/24" } -variable "k8s_service_ip" { default = "10.3.0.1" } -variable "dns_service_ip" { default = "10.3.0.10" } variable "allow_ssh_cidr" { default = "0.0.0.0/0" } +# VM Image and size variable "admin_username" { default = "core" } variable "aws_image_ami" { default = "ami-fde3e09e"} # channel/stable type/hvm variable "aws_master_vm_size" { default = "m3.medium" } variable "aws_worker_vm_size" { default = "m3.medium" } variable "aws_bastion_vm_size" { default = "t2.nano" } +# Kubernetes +variable "cluster_domain" { default = "cluster.local" } +variable "pod_cidr" { default = "10.2.0.0/16" } +variable "service_cidr" { default = "10.3.0.0/24" } +variable "k8s_service_ip" { default = "10.3.0.1" } +variable "dns_service_ip" { default = "10.3.0.10" } +variable "master_node_count" { default = "3" } +variable "worker_node_count" { default = "3" } +variable "worker_node_min" { default = "3" } +variable "worker_node_max" { default = "5" } + +# Deployment Artifact Versions +# Hyperkube # Set from https://quay.io/repository/coreos/hyperkube?tab=tags variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} variable "kubelet_image_tag" { default = "v1.5.1_coreos.0"} -variable "data_dir" { default = "/cncf/data/aws" } diff --git a/aws/modules.tf b/aws/modules.tf index 0ef24b1..e871060 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -22,18 +22,18 @@ module "iam" { } -module "route53" { - source = "./modules/route53" +module "dns" { + source = "./modules/dns" name = "${ var.name }" - etcd_ips = "${ var.etcd_ips }" + master_ips = "${ module.etcd.master_ips }" internal_tld = "${ var.internal_tld }" vpc_id = "${ module.vpc.id }" } module "etcd" { source = "./modules/etcd" - depends_id = "${ module.route53.depends_id }" + #depends_id = "${ module.dns.depends_id }" instance_profile_name = "${ module.iam.instance_profile_name_master }" name = "${ var.name }" @@ -43,7 +43,6 @@ module "etcd" { kubelet_image_url = "${ var.kubelet_image_url }" kubelet_image_tag = "${ var.kubelet_image_tag }" dns_service_ip = "${ var.dns_service_ip }" - etcd_ips = "${ var.etcd_ips }" etcd_security_group_id = "${ module.security.etcd_id }" external_elb_security_group_id = "${ module.security.external_elb_id }" instance_type = "${ var.aws_master_vm_size }" @@ -77,14 +76,14 @@ module "bastion" { module "worker" { source = "./modules/worker" - depends_id = "${ module.route53.depends_id }" + depends_id = "${ module.dns.depends_id }" instance_profile_name = "${ module.iam.instance_profile_name_worker }" ami_id = "${ var.aws_image_ami }" capacity = { - desired = 3 - max = 5 - min = 3 + desired = "${ var.worker_node_count}" + max = "${ var.worker_node_max}" + min = "${ var.worker_node_min}" } cluster_domain = "${ var.cluster_domain }" kubelet_image_url = "${ var.kubelet_image_url }" diff --git a/aws/modules/route53/route53.tf b/aws/modules/dns/dns.tf similarity index 88% rename from aws/modules/route53/route53.tf rename to aws/modules/dns/dns.tf index 3e456c1..eeb3d8d 100644 --- a/aws/modules/route53/route53.tf +++ b/aws/modules/dns/dns.tf @@ -11,21 +11,18 @@ resource "aws_route53_zone" "internal" { resource "aws_route53_record" "A-etcd" { name = "etcd" - records = [ "${ split(",", var.etcd_ips) }" ] + records = [ "${ var.master_ips }" ] ttl = "300" type = "A" zone_id = "${ aws_route53_zone.internal.zone_id }" } resource "aws_route53_record" "A-etcds" { - count = "${ length( split(",", var.etcd_ips) ) }" - name = "etcd${ count.index+1 }" + count = "${ var.master_node_count }" ttl = "300" type = "A" - records = [ - "${ element(split(",", var.etcd_ips), count.index) }" - ] + records = [ "${ element(var.master_ips, count.index) }" ] zone_id = "${ aws_route53_zone.internal.zone_id }" } diff --git a/aws/modules/dns/input.tf b/aws/modules/dns/input.tf new file mode 100644 index 0000000..23d1b0a --- /dev/null +++ b/aws/modules/dns/input.tf @@ -0,0 +1,6 @@ +variable "internal_tld" {} +variable "name" {} +variable "vpc_id" {} +variable "master_ips" {} +variable "master_node_count" {} + diff --git a/aws/modules/route53/io.tf b/aws/modules/dns/output.tf similarity index 72% rename from aws/modules/route53/io.tf rename to aws/modules/dns/output.tf index cfcc1ce..2c05297 100644 --- a/aws/modules/route53/io.tf +++ b/aws/modules/dns/output.tf @@ -1,8 +1,3 @@ -variable "etcd_ips" {} -variable "internal_tld" {} -variable "name" {} -variable "vpc_id" {} - output "depends_id" { value = "${null_resource.dummy_dependency.id}" } output "internal_name_servers" { value = "${ aws_route53_zone.internal.name_servers }" } output "internal_zone_id" { value = "${ aws_route53_zone.internal.zone_id }" } diff --git a/aws/modules/etcd/cloud-config.tf b/aws/modules/etcd/cloud-config.tf index 68d39ff..9aed236 100644 --- a/aws/modules/etcd/cloud-config.tf +++ b/aws/modules/etcd/cloud-config.tf @@ -35,7 +35,7 @@ resource "gzip_me" "kube-apiserver" { } data "template_file" "cloud-config" { - count = "${ length( split(",", var.etcd_ips) ) }" + count = "${ var.master_node_count }" template = "${ file( "${ path.module }/cloud-config.yml" )}" vars { diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 78e1e68..3149048 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -1,12 +1,11 @@ resource "aws_instance" "etcd" { - count = "${ length( split(",", var.etcd_ips) ) }" + count = "${ var.master_node_count }" ami = "${ var.ami_id }" associate_public_ip_address = false iam_instance_profile = "${ var.instance_profile_name }" instance_type = "${ var.instance_type }" key_name = "${ var.key_name }" - private_ip = "${ element(split(",", var.etcd_ips), count.index) }" root_block_device { volume_size = 124 diff --git a/aws/modules/etcd/input.tf b/aws/modules/etcd/input.tf index e3edb91..db88235 100644 --- a/aws/modules/etcd/input.tf +++ b/aws/modules/etcd/input.tf @@ -4,7 +4,6 @@ variable "kubelet_image_url" {} variable "kubelet_image_tag" {} variable "depends_id" {} variable "dns_service_ip" {} -variable "etcd_ips" {} variable "etcd_security_group_id" {} variable "external_elb_security_group_id" {} variable "instance_type" {} @@ -22,5 +21,5 @@ variable "k8s_etcd" {} variable "k8s_etcd_key" {} variable "k8s_apiserver" {} variable "k8s_apiserver_key" {} -# variable "s3_bucket" {} variable "instance_profile_name" {} +variable "master_node_count" {} diff --git a/aws/modules/etcd/output.tf b/aws/modules/etcd/output.tf index 74c1fb0..5bcb0c8 100644 --- a/aws/modules/etcd/output.tf +++ b/aws/modules/etcd/output.tf @@ -2,3 +2,5 @@ output "depends_id" { value = "${ null_resource.dummy_dependency.id }" } output "external_elb" { value = "${ aws_elb.external.dns_name }" } output "internal_ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } + +output "master_ips" { value = ["${ aws_instance.etcd.*.private_ip }"] } diff --git a/azure/input.tf b/azure/input.tf index 314fe2e..00ef11e 100644 --- a/azure/input.tf +++ b/azure/input.tf @@ -3,25 +3,33 @@ variable "name" { default = "azure" } variable "internal_tld" { default = "azure.cncf.demo" } variable "data_dir" { default = "/cncf/data/azure" } -variable "master_node_count" { default = "3" } -variable "worker_node_count" { default = "3" } -variable "master_vm_size" { default = "Standard_A2" } -variable "worker_vm_size" { default = "Standard_A2" } -variable "bastion_vm_size" { default = "Standard_A2" } -# Set from https://quay.io/repository/coreos/hyperkube?tab=tags -variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} -variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} +# Azure Cloud Specific Settings +variable "location" { default = "westus" } + +# VM Image and size +variable "admin_username" { default = "cncf"} variable "image_publisher" { default = "CoreOS" } variable "image_offer" { default = "CoreOS" } variable "image_sku" { default = "Stable" } variable "image_version" { default = "1298.6.0" } -variable "location" { default = "westus" } +variable "master_vm_size" { default = "Standard_A2" } +variable "worker_vm_size" { default = "Standard_A2" } +variable "bastion_vm_size" { default = "Standard_A2" } + +# Kubernetes variable "cluster_domain" { default = "cluster.local" } -variable "admin_username" { default = "cncf"} -variable "vpc_cidr" { default = "10.0.0.0/16" } variable "pod_cidr" { default = "10.2.0.0/16" } variable "service_cidr" { default = "10.3.0.0/24" } variable "k8s_service_ip" { default = "10.3.0.1" } variable "dns_service_ip" { default = "10.3.0.10" } -variable "allow_ssh_cidr" { default = "0.0.0.0/0" } +variable "master_node_count" { default = "3" } +variable "worker_node_count" { default = "3" } +# Autoscaling not supported by Kuberenetes on Azure yet +# variable "worker_node_min" { default = "3" } +# variable "worker_node_max" { default = "5" } +# Deployment Artifact Versions +# Hyperkube +# Set from https://quay.io/repository/coreos/hyperkube?tab=tags +variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} +variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} From c4de981e1f3e6c899f9458cf081396f4ba8a45bc Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 18 Apr 2017 06:16:34 +1200 Subject: [PATCH 045/149] Write terraformrc during Docker Build --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index b9b5c88..cc73494 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,6 +37,11 @@ go get -u github.com/cloudflare/cfssl/cmd/... # Install Gzip+base64 Provider RUN go get -u github.com/jakexks/terraform-provider-gzip +RUN echo providers { >> ~/.terraformrc +RUN echo " gzip = "terraform-provider-gzip"" >> ~/.terraformrc +RUN echo } >> ~/.terraformrc + + #Add Terraform Modules From a73abfd5ea40dafa5118ad7b50f284df79b639c6 Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 18 Apr 2017 06:17:16 +1200 Subject: [PATCH 046/149] Remove Write from Entrypoint --- entrypoint.sh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index eab357c..99cf615 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -6,21 +6,12 @@ set -e RED='\033[0;31m' NC='\033[0m' # No Color -function write_terraformrc { - cat <~/.terraformrc -providers { - gzip = "terraform-provider-gzip" -} -EOF -} - export TF_VAR_name="$2" export TF_VAR_internal_tld=${TF_VAR_name}.cncf.demo export TF_VAR_data_dir=/cncf/data/${TF_VAR_name} # Run CMD if [ "$1" = "aws-deploy" ] ; then - write_terraformrc terraform get /build/aws && \ terraform apply -target null_resource.ssl_gen /build/aws && \ time terraform apply /deploy/aws && \ From a3686005696fc03981a28d771d714bc2b567046c Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 18 Apr 2017 06:29:52 +1200 Subject: [PATCH 047/149] Fix Terraformrc Syntax --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cc73494..35975d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ go get -u github.com/cloudflare/cfssl/cmd/... # Install Gzip+base64 Provider RUN go get -u github.com/jakexks/terraform-provider-gzip RUN echo providers { >> ~/.terraformrc -RUN echo " gzip = "terraform-provider-gzip"" >> ~/.terraformrc +RUN echo ' gzip = "terraform-provider-gzip"' >> ~/.terraformrc RUN echo } >> ~/.terraformrc From ae9ae5146f2a76eb03c4db8bb38645e5cf1736fc Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 18 Apr 2017 06:49:43 +1200 Subject: [PATCH 048/149] terraform gzip provider one-liner --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 35975d9..78f38cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,10 +36,10 @@ RUN go get -u github.com/cloudflare/cfssl/cmd/cfssl && \ go get -u github.com/cloudflare/cfssl/cmd/... # Install Gzip+base64 Provider -RUN go get -u github.com/jakexks/terraform-provider-gzip -RUN echo providers { >> ~/.terraformrc -RUN echo ' gzip = "terraform-provider-gzip"' >> ~/.terraformrc -RUN echo } >> ~/.terraformrc +RUN go get -u github.com/jakexks/terraform-provider-gzip && \ + echo providers { >> ~/.terraformrc && \ + echo ' gzip = "terraform-provider-gzip"' >> ~/.terraformrc && \ + echo } >> ~/.terraformrc From 31b9b1c6333f5fe64312c46975c694c6bacb06f5 Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 18 Apr 2017 09:06:34 +1200 Subject: [PATCH 049/149] Update Name_servers_file to be based on Name --- azure/modules/dns/input.tf | 2 +- azure/modules/kubeconfig/output.tf | 1 - azure/modules/network/input.tf | 2 +- azure/modules/network/virtual_network.tf | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 azure/modules/kubeconfig/output.tf diff --git a/azure/modules/dns/input.tf b/azure/modules/dns/input.tf index 8f1d770..09d721f 100644 --- a/azure/modules/dns/input.tf +++ b/azure/modules/dns/input.tf @@ -1,5 +1,5 @@ variable "internal_tld" {} variable "name" {} -variable "name_servers_file" { default = "azure_dns_zone"} +variable "name_servers_file" {} variable "master_ips" { type = "list" } variable "master_node_count" {} diff --git a/azure/modules/kubeconfig/output.tf b/azure/modules/kubeconfig/output.tf deleted file mode 100644 index 4e7f3d7..0000000 --- a/azure/modules/kubeconfig/output.tf +++ /dev/null @@ -1 +0,0 @@ -output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/azure/modules/network/input.tf b/azure/modules/network/input.tf index 3946d3a..6ff315e 100644 --- a/azure/modules/network/input.tf +++ b/azure/modules/network/input.tf @@ -1,4 +1,4 @@ -variable "cidr" {} +variable "vpc_cidr" {} variable "name" {} variable "name_servers_file" {} variable "location" {} diff --git a/azure/modules/network/virtual_network.tf b/azure/modules/network/virtual_network.tf index 1ea0582..4cd5910 100644 --- a/azure/modules/network/virtual_network.tf +++ b/azure/modules/network/virtual_network.tf @@ -16,7 +16,7 @@ resource "azurerm_subnet" "cncf" { resource "azurerm_virtual_network" "cncf" { name = "${ var.name }" resource_group_name = "${ var.name }" - address_space = ["${ var.cidr }"] + address_space = ["${ var.vpc_cidr }"] location = "${ var.location }" dns_servers = [ "${ element(split( ",", file(var.name_servers_file) ),0) }", From b505a8f357e02f19e9714454b792e25cd9305a53 Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 18 Apr 2017 09:09:54 +1200 Subject: [PATCH 050/149] Update cidr VAR --- azure/input.tf | 1 + azure/modules.tf | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/azure/input.tf b/azure/input.tf index 00ef11e..66aeb3b 100644 --- a/azure/input.tf +++ b/azure/input.tf @@ -5,6 +5,7 @@ variable "data_dir" { default = "/cncf/data/azure" } # Azure Cloud Specific Settings variable "location" { default = "westus" } +variable "vpc_cidr" { default = "10.0.0.0/16" } # VM Image and size variable "admin_username" { default = "cncf"} diff --git a/azure/modules.tf b/azure/modules.tf index 31ff773..4df1364 100644 --- a/azure/modules.tf +++ b/azure/modules.tf @@ -1,7 +1,7 @@ module "network" { source = "./modules/network" name = "${ var.name }" - cidr = "${ var.vpc_cidr }" + vpc_cidr = "${ var.vpc_cidr }" name_servers_file = "${ module.dns.name_servers_file }" location = "${ var.location }" } @@ -12,6 +12,8 @@ module "dns" { internal_tld = "${ var.internal_tld }" master_ips = "${ module.etcd.master_ips }" master_node_count = "${ var.master_node_count }" + name_servers_file = "${ var.data_dir }/dns" + } module "etcd" { From 3a967ff4e7f7edd4048feb7bf32c74a37c6ba074 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 18 Apr 2017 12:11:51 +1200 Subject: [PATCH 051/149] Initial stab at packet deploy --- Dockerfile | 7 ++- packet/bastion-node.tf | 17 ++++++ packet/bastion-user-data.yml | 14 +++++ packet/init-cfssl | 111 +++++++++++++++++++++++++++++++++++ packet/input.tf | 39 ++++++++++++ packet/output.tf | 8 +++ packet/packet.tf | 13 ++++ packet/ssl-ssh-cloud.tf | 42 +++++++++++++ 8 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 packet/bastion-node.tf create mode 100644 packet/bastion-user-data.yml create mode 100755 packet/init-cfssl create mode 100644 packet/input.tf create mode 100644 packet/output.tf create mode 100644 packet/packet.tf create mode 100644 packet/ssl-ssh-cloud.tf diff --git a/Dockerfile b/Dockerfile index 78f38cb..eccf7d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ ENV KUBECTL_VERSION=v1.5.2 ENV GCLOUD_VERSION=150.0.0 ENV AWSCLI_VERSION=1.11.75 ENV AZURECLI_VERSION=2.0.2 +ENV PACKETCLI_VERSION=1.33 ENV TERRAFORM_VERSION=0.9.3 ENV ARC=amd64 @@ -12,17 +13,21 @@ RUN apk update RUN apk add --update git bash util-linux wget tar curl build-base jq \ py-pip groff less openssh bind-tools python python-dev libffi-dev openssl-dev +# no way to pin this packet-cli at the moment +RUN go get -u github.com/ebsarr/packet +RUN pip install packet-python==${PACKETCLI_VERSION} argh tabulate RUN pip install azure-cli==${AZURECLI_VERSION} RUN pip install awscli==${AWSCLI_VERSION} RUN apk --purge -v del py-pip && \ rm /var/cache/apk/* -#Install Google Cloud SDK +# Install Google Cloud SDK RUN wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${GCLOUD_VERSION}-linux-x86.tar.gz && \ tar xvfz google-cloud-sdk-${GCLOUD_VERSION}-linux-x86.tar.gz && \ ./google-cloud-sdk/install.sh -q + #Install Kubectl RUN wget -O /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/$ARC/kubectl && \ chmod +x /usr/local/bin/kubectl diff --git a/packet/bastion-node.tf b/packet/bastion-node.tf new file mode 100644 index 0000000..6dce406 --- /dev/null +++ b/packet/bastion-node.tf @@ -0,0 +1,17 @@ +resource "packet_device" "bastion" { + hostname = "bastion" + # baremental_0-3 storage_1-2 + plan = "baremetal_0" + facility = "${ var.packet_facility }" + operating_system = "coreos_stable" + billing_cycle = "hourly" + project_id = "${ var.packet_project_id }" + user_data = "${ data.template_file.bastion-user-data.rendered }" +} + +data "template_file" "bastion-user-data" { + template = "${ file( "${ path.module }/bastion-user-data.yml" )}" + vars { + internal_tld = "${ var.internal_tld }" + } +} diff --git a/packet/bastion-user-data.yml b/packet/bastion-user-data.yml new file mode 100644 index 0000000..41ece8a --- /dev/null +++ b/packet/bastion-user-data.yml @@ -0,0 +1,14 @@ +#cloud-config + +--- +coreos: + update: + reboot-strategy: etcd-lock + + etcd2: + discovery-srv: ${ internal_tld } + proxy: on + + units: + - name: etcd2.service + command: start diff --git a/packet/init-cfssl b/packet/init-cfssl new file mode 100755 index 0000000..9bd9e49 --- /dev/null +++ b/packet/init-cfssl @@ -0,0 +1,111 @@ +#!/bin/bash -e + +function usage { cat <ca-csr.json +echo "$(ca-config)" >ca-config.json + +# generate ca +cfssl gencert -initca ca-csr.json | cfssljson -bare ca - +_chmod ca + +# generate keys and certs +generate k8s-admin client-server "${DEFAULT_HOSTS}" +generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},master.${INTERNAL_TLD},*.${REGION}.cloudapp.azure.com" +generate k8s-etcd client-server "etcd.${INTERNAL_TLD},etcd1.${INTERNAL_TLD},etcd2.${INTERNAL_TLD},etcd3.${INTERNAL_TLD}" +generate k8s-worker client "${DEFAULT_HOSTS}" + +# TODO: fix cert provisioning hacks +#tar -rf k8s-apiserver.tar k8s-etcd.pem k8s-etcd-key.pem +#tar -rf k8s-worker.tar ca.pem +#bzip2 k8s-apiserver.tar +#bzip2 k8s-worker.tar diff --git a/packet/input.tf b/packet/input.tf new file mode 100644 index 0000000..7ceeff8 --- /dev/null +++ b/packet/input.tf @@ -0,0 +1,39 @@ +variable "name" { default = "packet" } +# required for now +variable "packet_project_id" { } + +variable "internal_tld" { default = "packet.cncf.demo" } +variable "data_dir" { default = "/cncf/data/packet" } + +# Azure Cloud Specific Settings +# New York Metro (EWR1) +# Silicon Valley (SJC1) +# Amsterdam, NL (AMS1) +# Tokyo, JP (NRT1) +variable "packet_facility" { default = "nrt1" } + +# VM Image and size +variable "admin_username" { default = "cncf"} +# https://www.terraform.io/docs/providers/packet/r/device.html#operating_system-1 +variable "packet_operating_system" { default = "1298.6.0" } +variable "master_vm_size" { default = "Standard_A2" } +variable "worker_vm_size" { default = "Standard_A2" } +variable "bastion_vm_size" { default = "Standard_A2" } + +# Kubernetes +variable "cluster_domain" { default = "cluster.local" } +variable "pod_cidr" { default = "10.2.0.0/16" } +variable "service_cidr" { default = "10.3.0.0/24" } +variable "k8s_service_ip" { default = "10.3.0.1" } +variable "dns_service_ip" { default = "10.3.0.10" } +variable "master_node_count" { default = "3" } +variable "worker_node_count" { default = "3" } +# Autoscaling not supported by Kuberenetes on Azure yet +# variable "worker_node_min" { default = "3" } +# variable "worker_node_max" { default = "5" } + +# Deployment Artifact Versions +# Hyperkube +# Set from https://quay.io/repository/coreos/hyperkube?tab=tags +variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} +variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} diff --git a/packet/output.tf b/packet/output.tf new file mode 100644 index 0000000..80b1e33 --- /dev/null +++ b/packet/output.tf @@ -0,0 +1,8 @@ +# output "fqdn_k8s" { value = "${ module.etcd.fqdn_lb}" } +# output "bastion_ip" { value = "${ module.bastion.bastion_ip}" } +# output "bastion_fqdn" { value = "${ module.bastion.bastion_fqdn}" } +# output "k8s_admin" { value = "${ k8s_admin}"} +# # fixme for use outside container +output "ssh_key_setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir }/.ssh/id_rsa"} +output "ssh_via_bastion" { value = "ssh -At core@${ packet_device.bastion.network.0.address }" } +# " ssh ${ var.admin_username }@etcd1.${ var.internal_tld }"} diff --git a/packet/packet.tf b/packet/packet.tf new file mode 100644 index 0000000..a438b9c --- /dev/null +++ b/packet/packet.tf @@ -0,0 +1,13 @@ +# Configure the Packet Provider +# https://www.terraform.io/docs/providers/packet/index.html +# PACKET_AUTH_TOKEN must be set +# resource "packet_project" "cncf" { +# name = "${ var.name }" +# # packet_project.cncf: Payment method can't be blank +# payment_method = "" +# } + +resource "packet_ssh_key" "cncf" { + name = "${ var.name }" + public_key = "${file("${ var.data_dir }/.ssh/id_rsa.pub")}" +} diff --git a/packet/ssl-ssh-cloud.tf b/packet/ssl-ssh-cloud.tf new file mode 100644 index 0000000..75b35a4 --- /dev/null +++ b/packet/ssl-ssh-cloud.tf @@ -0,0 +1,42 @@ +#Gen Certs and SSH KeyPair +resource "null_resource" "ssl_ssh_cloud_gen" { + + provisioner "local-exec" { + command = < Date: Wed, 19 Apr 2017 08:09:14 +1200 Subject: [PATCH 052/149] 3 etcd/masters and 3 workers coming up on packet --- packet/bastion-node.tf | 9 +- packet/input.tf | 27 ++- packet/modules.tf | 91 +++++++++ packet/modules/etcd/discovery.tf | 18 ++ packet/modules/etcd/etcd-cloud-config.tf | 62 ++++++ packet/modules/etcd/etcd-cloud-config.yml | 224 ++++++++++++++++++++++ packet/modules/etcd/etcd-nodes.tf | 10 + packet/modules/etcd/input.tf | 21 ++ packet/modules/etcd/kube-apiserver.yml | 59 ++++++ packet/modules/etcd/output.tf | 5 + packet/output.tf | 6 + 11 files changed, 511 insertions(+), 21 deletions(-) create mode 100644 packet/modules.tf create mode 100644 packet/modules/etcd/discovery.tf create mode 100644 packet/modules/etcd/etcd-cloud-config.tf create mode 100644 packet/modules/etcd/etcd-cloud-config.yml create mode 100644 packet/modules/etcd/etcd-nodes.tf create mode 100644 packet/modules/etcd/input.tf create mode 100644 packet/modules/etcd/kube-apiserver.yml create mode 100644 packet/modules/etcd/output.tf diff --git a/packet/bastion-node.tf b/packet/bastion-node.tf index 6dce406..bbe8ab7 100644 --- a/packet/bastion-node.tf +++ b/packet/bastion-node.tf @@ -1,12 +1,11 @@ resource "packet_device" "bastion" { - hostname = "bastion" - # baremental_0-3 storage_1-2 - plan = "baremetal_0" - facility = "${ var.packet_facility }" + hostname = "bastion" + plan = "baremetal_0" operating_system = "coreos_stable" + user_data = "${ data.template_file.bastion-user-data.rendered }" billing_cycle = "hourly" + facility = "${ var.packet_facility }" project_id = "${ var.packet_project_id }" - user_data = "${ data.template_file.bastion-user-data.rendered }" } data "template_file" "bastion-user-data" { diff --git a/packet/input.tf b/packet/input.tf index 7ceeff8..77ee606 100644 --- a/packet/input.tf +++ b/packet/input.tf @@ -1,24 +1,19 @@ variable "name" { default = "packet" } -# required for now -variable "packet_project_id" { } + +# Set with env TF_VAR_packet_project_id +variable "packet_project_id" {} # required for now +variable "packet_facility" { default = "nrt1" } +variable "packet_billing_cycle" { default = "hourly" } +variable "packet_operating_system" { default = "coreos_stable" } +variable "packet_bastion_device_plan" { default = "baremetal_0" } +variable "packet_master_device_plan" { default = "baremetal_0" } +variable "packet_worker_device_plan" { default = "baremetal_0" } variable "internal_tld" { default = "packet.cncf.demo" } variable "data_dir" { default = "/cncf/data/packet" } -# Azure Cloud Specific Settings -# New York Metro (EWR1) -# Silicon Valley (SJC1) -# Amsterdam, NL (AMS1) -# Tokyo, JP (NRT1) -variable "packet_facility" { default = "nrt1" } - # VM Image and size -variable "admin_username" { default = "cncf"} -# https://www.terraform.io/docs/providers/packet/r/device.html#operating_system-1 -variable "packet_operating_system" { default = "1298.6.0" } -variable "master_vm_size" { default = "Standard_A2" } -variable "worker_vm_size" { default = "Standard_A2" } -variable "bastion_vm_size" { default = "Standard_A2" } +variable "admin_username" { default = "core"} # Kubernetes variable "cluster_domain" { default = "cluster.local" } @@ -34,6 +29,6 @@ variable "worker_node_count" { default = "3" } # Deployment Artifact Versions # Hyperkube -# Set from https://quay.io/repository/coreos/hyperkube?tab=tags +# Set from https://quay.io/repository/coreos/hyperkube variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} diff --git a/packet/modules.tf b/packet/modules.tf new file mode 100644 index 0000000..97a7ba5 --- /dev/null +++ b/packet/modules.tf @@ -0,0 +1,91 @@ +# module "network" { +# source = "./modules/network" +# name = "${ var.name }" +# cidr = "${ var.vpc_cidr }" +# name_servers_file = "${ module.dns.name_servers_file }" +# location = "${ var.location }" +# } + +# module "dns" { +# source = "./modules/dns" +# name = "${ var.name }" +# internal_tld = "${ var.internal_tld }" +# master_ips = "${ module.etcd.master_ips }" +# master_node_count = "${ var.master_node_count }" +# } + +module "etcd" { + source = "./modules/etcd" + name = "${ var.name }" + etcd_discovery = "${ var.data_dir }/etcd" + master_node_count = "${ var.master_node_count }" + packet_project_id = "${ var.packet_project_id }" + packet_facility = "${ var.packet_facility }" + packet_billing_cycle = "${ var.packet_billing_cycle }" + packet_operating_system = "${ var.packet_operating_system }" + packet_master_device_plan = "${ var.packet_master_device_plan }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + dns_service_ip = "${ var.dns_service_ip }" + cluster_domain = "${ var.cluster_domain }" + internal_tld = "${ var.internal_tld }" + pod_cidr = "${ var.pod_cidr }" + service_cidr = "${ var.service_cidr }" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s_etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" + k8s_etcd_key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" + k8s_apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" + k8s_apiserver_key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" + data_dir = "${ var.data_dir }" +} + +# module "bastion" { +# source = "./modules/bastion" +# name = "${ var.name }" +# location = "${ var.location }" +# bastion_vm_size = "${ var.bastion_vm_size }" +# image_publisher = "${ var.image_publisher }" +# image_offer = "${ var.image_offer }" +# image_sku = "${ var.image_sku }" +# image_version = "${ var.image_version }" +# admin_username = "${ var.admin_username }" +# subnet_id = "${ module.network.subnet_id }" +# storage_primary_endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" +# storage_container = "${ azurerm_storage_container.cncf.name }" +# availability_id = "${ azurerm_availability_set.cncf.id }" +# internal_tld = "${ var.internal_tld }" +# data_dir = "${ var.data_dir }" +# } + +module "worker" { + source = "./modules/worker" + name = "${ var.name }" + worker_node_count = "${ var.worker_node_count }" + packet_project_id = "${ var.packet_project_id }" + packet_facility = "${ var.packet_facility }" + packet_billing_cycle = "${ var.packet_billing_cycle }" + packet_operating_system = "${ var.packet_operating_system }" + packet_worker_device_plan = "${ var.packet_worker_device_plan }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + dns_service_ip = "${ var.dns_service_ip }" + cluster_domain = "${ var.cluster_domain }" + internal_tld = "${ var.internal_tld }" + pod_cidr = "${ var.pod_cidr }" + service_cidr = "${ var.service_cidr }" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s_worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" + k8s_worker_key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" + data_dir = "${ var.data_dir }" +} + +# module "kubeconfig" { +# source = "./modules/kubeconfig" + +# admin_key_pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" +# admin_pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" +# ca_pem = "${ var.data_dir }/.cfssl/ca.pem" +# data_dir = "${ var.data_dir }" +# fqdn_k8s = "${ module.etcd.fqdn_lb }" +# name = "${ var.name }" +# } diff --git a/packet/modules/etcd/discovery.tf b/packet/modules/etcd/discovery.tf new file mode 100644 index 0000000..a321d09 --- /dev/null +++ b/packet/modules/etcd/discovery.tf @@ -0,0 +1,18 @@ +#Get Discovery URL +resource "null_resource" "discovery_gen" { + + provisioner "local-exec" { + command = < ${ var.etcd_discovery } +EOF + } + + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = < Date: Wed, 19 Apr 2017 08:34:30 +1200 Subject: [PATCH 053/149] Store keys, certs, and tfstate per packet deploy --- entrypoint.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index 99cf615..77e1193 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -9,6 +9,9 @@ NC='\033[0m' # No Color export TF_VAR_name="$2" export TF_VAR_internal_tld=${TF_VAR_name}.cncf.demo export TF_VAR_data_dir=/cncf/data/${TF_VAR_name} +# tfstate, sslcerts, and ssh keys are currently stored in TF_VAR_data_dir +mkdir -p $TF_VAR_data_dir +cd $TF_VAR_data_dir # Run CMD if [ "$1" = "aws-deploy" ] ; then From 79c94cf53134233567d0518415b5f86246b04713 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 19 Apr 2017 08:35:34 +1200 Subject: [PATCH 054/149] Add packet entrypoint to deploy container --- entrypoint.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 77e1193..5586a7e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -38,6 +38,16 @@ elif [ "$1" = "azure-deploy" ] ; then printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} -elif [ "$1" = "destroy" ] ; then - time terraform destroy -force /delpoy/azure +elif [ "$1" = "azure-destroy" ] ; then + time terraform destroy -force /build/azure +elif [ "$1" = "packet-deploy" ] ; then + terraform get /build/packet && \ + terraform apply -target module.etcd.null_resource.discovery_gen /build/packet && \ + terraform apply -target null_resource.ssl_ssh_gen /build/packet && \ + time terraform apply /build/packet && \ + printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ + printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ + printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} +elif [ "$1" = "packet-destroy" ] ; then + time terraform destroy -force /build/packet fi From 986737fe01d6b41736c215dd9205d6e16dab26c8 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 19 Apr 2017 08:35:49 +1200 Subject: [PATCH 055/149] Remove bastion node from packet --- packet/bastion-node.tf | 16 ---------------- packet/bastion-user-data.yml | 14 -------------- 2 files changed, 30 deletions(-) delete mode 100644 packet/bastion-node.tf delete mode 100644 packet/bastion-user-data.yml diff --git a/packet/bastion-node.tf b/packet/bastion-node.tf deleted file mode 100644 index bbe8ab7..0000000 --- a/packet/bastion-node.tf +++ /dev/null @@ -1,16 +0,0 @@ -resource "packet_device" "bastion" { - hostname = "bastion" - plan = "baremetal_0" - operating_system = "coreos_stable" - user_data = "${ data.template_file.bastion-user-data.rendered }" - billing_cycle = "hourly" - facility = "${ var.packet_facility }" - project_id = "${ var.packet_project_id }" -} - -data "template_file" "bastion-user-data" { - template = "${ file( "${ path.module }/bastion-user-data.yml" )}" - vars { - internal_tld = "${ var.internal_tld }" - } -} diff --git a/packet/bastion-user-data.yml b/packet/bastion-user-data.yml deleted file mode 100644 index 41ece8a..0000000 --- a/packet/bastion-user-data.yml +++ /dev/null @@ -1,14 +0,0 @@ -#cloud-config - ---- -coreos: - update: - reboot-strategy: etcd-lock - - etcd2: - discovery-srv: ${ internal_tld } - proxy: on - - units: - - name: etcd2.service - command: start From 601cb07f00be21ffbb1831740dfdf98a0a3282ec Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 19 Apr 2017 08:40:56 +1200 Subject: [PATCH 056/149] Add packet worker nodes --- packet/modules/etcd/etcd-nodes.tf | 2 +- packet/modules/worker/input.tf | 18 ++ packet/modules/worker/output.tf | 5 + packet/modules/worker/worker-cloud-config.tf | 31 ++++ packet/modules/worker/worker-cloud-config.yml | 159 ++++++++++++++++++ packet/modules/worker/worker-nodes.tf | 10 ++ packet/output.tf | 1 - packet/ssl-ssh-cloud.tf | 4 +- 8 files changed, 226 insertions(+), 4 deletions(-) create mode 100644 packet/modules/worker/input.tf create mode 100644 packet/modules/worker/output.tf create mode 100644 packet/modules/worker/worker-cloud-config.tf create mode 100644 packet/modules/worker/worker-cloud-config.yml create mode 100644 packet/modules/worker/worker-nodes.tf diff --git a/packet/modules/etcd/etcd-nodes.tf b/packet/modules/etcd/etcd-nodes.tf index ce38881..9461ec1 100644 --- a/packet/modules/etcd/etcd-nodes.tf +++ b/packet/modules/etcd/etcd-nodes.tf @@ -1,5 +1,5 @@ resource "packet_device" "masters" { - hostname = "etcd-master${ count.index + 1 }" + hostname = "master-${ count.index + 1 }-${ var.name }" count = "${ var.master_node_count }" facility = "${ var.packet_facility }" project_id = "${ var.packet_project_id }" diff --git a/packet/modules/worker/input.tf b/packet/modules/worker/input.tf new file mode 100644 index 0000000..f5e6f65 --- /dev/null +++ b/packet/modules/worker/input.tf @@ -0,0 +1,18 @@ +variable "name" {} +variable "worker_node_count" {} +variable "packet_facility" {} +variable "packet_project_id" {} +variable "packet_billing_cycle" {} +variable "packet_operating_system" {} +variable "packet_worker_device_plan" {} +variable "kubelet_image_url" {} +variable "kubelet_image_tag" {} +variable "dns_service_ip" {} +variable "cluster_domain" {} +variable "internal_tld" {} +variable "pod_cidr" {} +variable "service_cidr" {} +variable "ca" {} +variable "k8s_worker" {} +variable "k8s_worker_key" {} +variable "data_dir" {} diff --git a/packet/modules/worker/output.tf b/packet/modules/worker/output.tf new file mode 100644 index 0000000..4f0e4a7 --- /dev/null +++ b/packet/modules/worker/output.tf @@ -0,0 +1,5 @@ +#output "fqdn_lb" { value = "${azurerm_public_ip.cncf.fqdn}" } +output "first_worker_ip" { value = "${ packet_device.workers.0.network.0.address }" } +output "second_worker_ip" { value = "${ packet_device.workers.1.network.0.address }" } +output "third_worker_ip" { value = "${ packet_device.workers.2.network.0.address }" } +output "worker_ips" { value = ["${ packet_device.workers.*.network.0.address }"] } diff --git a/packet/modules/worker/worker-cloud-config.tf b/packet/modules/worker/worker-cloud-config.tf new file mode 100644 index 0000000..faaf91e --- /dev/null +++ b/packet/modules/worker/worker-cloud-config.tf @@ -0,0 +1,31 @@ +provider "gzip" { + compressionlevel = "BestCompression" +} + +resource "gzip_me" "ca" { + input = "${ var.ca }" +} + +resource "gzip_me" "k8s_worker" { + input = "${ var.k8s_worker }" +} + +resource "gzip_me" "k8s_worker_key" { + input = "${ var.k8s_worker_key }" +} + +data "template_file" "worker_user_data" { + template = "${ file( "${ path.module }/worker-cloud-config.yml" )}" + + vars { + cluster_domain = "${ var.cluster_domain }" + internal_tld = "${ var.internal_tld }" + dns_service_ip = "${ var.dns_service_ip }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + ca = "${ gzip_me.ca.output }" + k8s_worker = "${ gzip_me.k8s_worker.output }" + k8s_worker_key = "${ gzip_me.k8s_worker_key.output }" + } +} + diff --git a/packet/modules/worker/worker-cloud-config.yml b/packet/modules/worker/worker-cloud-config.yml new file mode 100644 index 0000000..4a160b7 --- /dev/null +++ b/packet/modules/worker/worker-cloud-config.yml @@ -0,0 +1,159 @@ +#cloud-config +--- +coreos: + + etcd2: + discovery-srv: ${ internal_tld } + peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem + peer-client-cert-auth: true + peer-cert-file: /etc/kubernetes/ssl/k8s-worker.pem + peer-key-file: /etc/kubernetes/ssl/k8s-worker-key.pem + proxy: on + + units: + - name: etcd2.service + command: start + + - name: docker.service + command: start + + - name: kubelet.service + command: start + content: | + [Unit] + ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper + [Service] + Environment="KUBELET_IMAGE_URL=${ kubelet_image_url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet_image_tag }" + Environment="RKT_OPTS=\ + --volume dns,kind=host,source=/etc/resolv.conf \ + --mount volume=dns,target=/etc/resolv.conf \ + --volume rkt,kind=host,source=/opt/bin/host-rkt \ + --mount volume=rkt,target=/usr/bin/rkt \ + --volume var-lib-rkt,kind=host,source=/var/lib/rkt \ + --mount volume=var-lib-rkt,target=/var/lib/rkt \ + --volume stage,kind=host,source=/tmp \ + --mount volume=stage,target=/tmp \ + --volume var-log,kind=host,source=/var/log \ + --mount volume=var-log,target=/var/log" + ExecStartPre=/usr/bin/mkdir -p /var/log/containers + ExecStartPre=/usr/bin/mkdir -p /var/lib/kubelet + ExecStartPre=/usr/bin/mount --bind /var/lib/kubelet /var/lib/kubelet + ExecStartPre=/usr/bin/mount --make-shared /var/lib/kubelet + ExecStart=/usr/lib/coreos/kubelet-wrapper \ + --allow-privileged=true \ + --api-servers=http://master.${ internal_tld }:8080 \ + --cloud-provider=azure \ + --cloud-config=/etc/kubernetes/ssl/azure-config.json \ + --cluster-dns=${ dns_service_ip } \ + --cluster_domain=${ cluster_domain } \ + --config=/etc/kubernetes/manifests \ + --kubeconfig=/etc/kubernetes/kubeconfig.yml \ + --register-node=true \ + --tls-cert-file=/etc/kubernetes/ssl/k8s-worker.pem \ + --tls-private-key-file=/etc/kubernetes/ssl/k8s-worker-key.pem + Restart=always + RestartSec=5 + [Install] + WantedBy=multi-user.target + + update: + reboot-strategy: etcd-lock + +write-files: + - path: /opt/bin/host-rkt + permissions: 0755 + owner: root:root + content: | + #!/bin/sh + exec nsenter -m -u -i -n -p -t 1 -- /usr/bin/rkt "$@" + + - path: /etc/kubernetes/kubeconfig.yml + content: | + apiVersion: v1 + kind: Config + clusters: + - name: local + cluster: + certificate-authority: /etc/kubernetes/ssl/ca.pem + users: + - name: kubelet + user: + client-certificate: /etc/kubernetes/ssl/k8s-worker.pem + client-key: /etc/kubernetes/ssl/k8s-worker-key.pem + contexts: + - context: + cluster: local + user: kubelet + name: kubelet-context + current-context: kubelet-context + + - path: /etc/kubernetes/manifests/kube-proxy.yml + content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-proxy + namespace: kube-system + spec: + hostNetwork: true + containers: + - name: kube-proxy + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - proxy + - --kubeconfig=/etc/kubernetes/kubeconfig.yml + - --master=https://master.${ internal_tld } + - --proxy-mode=iptables + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/ssl/certs + name: "ssl-certs" + - mountPath: /etc/kubernetes/kubeconfig.yml + name: "kubeconfig" + readOnly: true + - mountPath: /etc/kubernetes/ssl + name: "etc-kube-ssl" + readOnly: true + volumes: + - name: "ssl-certs" + hostPath: + path: "/usr/share/ca-certificates" + - name: "kubeconfig" + hostPath: + path: "/etc/kubernetes/kubeconfig.yml" + - name: "etc-kube-ssl" + hostPath: + path: "/etc/kubernetes/ssl" + + - path: /etc/logrotate.d/docker-containers + content: | + /var/lib/docker/containers/*/*.log { + rotate 7 + daily + compress + size=1M + missingok + delaycompress + copytruncate + } + + - path: /etc/kubernetes/ssl/ca.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ ca } + + - path: /etc/kubernetes/ssl/k8s-worker.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s_worker } + + - path: /etc/kubernetes/ssl/k8s-worker-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s_worker_key } diff --git a/packet/modules/worker/worker-nodes.tf b/packet/modules/worker/worker-nodes.tf new file mode 100644 index 0000000..5e18d7a --- /dev/null +++ b/packet/modules/worker/worker-nodes.tf @@ -0,0 +1,10 @@ +resource "packet_device" "workers" { + hostname = "worker-${ count.index + 1 }-${ var.name }" + count = "${ var.worker_node_count }" + facility = "${ var.packet_facility }" + project_id = "${ var.packet_project_id }" + plan = "${ var.packet_worker_device_plan }" + billing_cycle = "${ var.packet_billing_cycle }" + operating_system = "${ var.packet_operating_system }" + user_data = "${ element(data.template_file.worker_user_data.*.rendered, count.index) }" +} diff --git a/packet/output.tf b/packet/output.tf index 156c6c0..6daf46b 100644 --- a/packet/output.tf +++ b/packet/output.tf @@ -4,7 +4,6 @@ # output "k8s_admin" { value = "${ k8s_admin}"} # # fixme for use outside container output "ssh_key_setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir }/.ssh/id_rsa"} -output "ssh_via_bastion" { value = "ssh -At core@${ packet_device.bastion.network.0.address }" } output "ssh_first_master" { value = "ssh -At core@${ module.etcd.first_master_ip }" } output "ssh_second_master" { value = "ssh -At core@${ module.etcd.second_master_ip }" } output "ssh_third_master" { value = "ssh -At core@${ module.etcd.third_master_ip }" } diff --git a/packet/ssl-ssh-cloud.tf b/packet/ssl-ssh-cloud.tf index 75b35a4..49efa32 100644 --- a/packet/ssl-ssh-cloud.tf +++ b/packet/ssl-ssh-cloud.tf @@ -1,5 +1,5 @@ #Gen Certs and SSH KeyPair -resource "null_resource" "ssl_ssh_cloud_gen" { +resource "null_resource" "ssl_ssh_gen" { provisioner "local-exec" { command = < Date: Wed, 19 Apr 2017 12:08:20 +1200 Subject: [PATCH 057/149] Using cncf.ci domain on dnsimple for Packet --- packet/input.tf | 2 +- packet/modules.tf | 18 +++---- packet/modules/dns/dns.tf | 60 +++++++++++++++++++++++ packet/modules/dns/input.tf | 5 ++ packet/modules/dns/output.tf | 0 packet/modules/etcd/etcd-cloud-config.yml | 8 +-- packet/ssl-ssh-cloud.tf | 4 +- 7 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 packet/modules/dns/dns.tf create mode 100644 packet/modules/dns/input.tf create mode 100644 packet/modules/dns/output.tf diff --git a/packet/input.tf b/packet/input.tf index 77ee606..5fb6d87 100644 --- a/packet/input.tf +++ b/packet/input.tf @@ -9,7 +9,7 @@ variable "packet_bastion_device_plan" { default = "baremetal_0" } variable "packet_master_device_plan" { default = "baremetal_0" } variable "packet_worker_device_plan" { default = "baremetal_0" } -variable "internal_tld" { default = "packet.cncf.demo" } +variable "domain" { default = "cncf.ci" } variable "data_dir" { default = "/cncf/data/packet" } # VM Image and size diff --git a/packet/modules.tf b/packet/modules.tf index 97a7ba5..abb9d8a 100644 --- a/packet/modules.tf +++ b/packet/modules.tf @@ -6,13 +6,13 @@ # location = "${ var.location }" # } -# module "dns" { -# source = "./modules/dns" -# name = "${ var.name }" -# internal_tld = "${ var.internal_tld }" -# master_ips = "${ module.etcd.master_ips }" -# master_node_count = "${ var.master_node_count }" -# } +module "dns" { + source = "./modules/dns" + name = "${ var.name }" + master_ips = "${ module.etcd.master_ips }" + master_node_count = "${ var.master_node_count }" + domain = "${ var.domain }" +} module "etcd" { source = "./modules/etcd" @@ -28,7 +28,7 @@ module "etcd" { kubelet_image_tag = "${ var.kubelet_image_tag }" dns_service_ip = "${ var.dns_service_ip }" cluster_domain = "${ var.cluster_domain }" - internal_tld = "${ var.internal_tld }" + internal_tld = "${ var.name }.${ var.domain }" pod_cidr = "${ var.pod_cidr }" service_cidr = "${ var.service_cidr }" ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" @@ -70,7 +70,7 @@ module "worker" { kubelet_image_tag = "${ var.kubelet_image_tag }" dns_service_ip = "${ var.dns_service_ip }" cluster_domain = "${ var.cluster_domain }" - internal_tld = "${ var.internal_tld }" + internal_tld = "${ var.name }.${ var.domain }" pod_cidr = "${ var.pod_cidr }" service_cidr = "${ var.service_cidr }" ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" diff --git a/packet/modules/dns/dns.tf b/packet/modules/dns/dns.tf new file mode 100644 index 0000000..266d6f0 --- /dev/null +++ b/packet/modules/dns/dns.tf @@ -0,0 +1,60 @@ +# domain - (Required) The domain to add the record to +# name - (Required) The name of the record +# value - (Required) The value of the record +# type - (Required) The type of the record +# ttl - (Optional) The TTL of the record +# priority - (Optional) The priority of the record - only useful for some record typesi + +resource "dnsimple_record" "A-etcd" { + count = "${ var.master_node_count }" + name = "etcd.${ var.name }" + value = "${ element(var.master_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + +resource "dnsimple_record" "A-etcds" { + count = "${ var.master_node_count }" + name = "etcd${ count.index+1 }.${ var.name }" + value = "${ element(var.master_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + +resource "dnsimple_record" "A-masters" { + count = "${ var.master_node_count }" + name = "master${ count.index+1 }.${ var.name }" + value = "${ element(var.master_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + +resource "dnsimple_record" "A-master" { + count = "${ var.master_node_count }" + name = "master.${ var.name }" + value = "${ element(var.master_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + +# resource "dnsimple_record" "etcd-client-tcp" { +# count = "${ var.master_node_count }" +# name = "_etcd-client._tcp.${ var.name }" +# value = "etcd${ count.index+1 }.${ var.name }.${ var.domain }." +# type = "SRV" +# ttl = "${ var.record_ttl }" +# domain = "${ var.domain }" +# } + +# resource "dnsimple_record" "etcd-server-tcp" { +# count = "${ var.master_node_count }" +# name = "_etcd-server-ssl._tcp.${ var.name }" +# value = "etcd${ count.index+1 }.${ var.name }.${ var.domain }." +# type = "SRV" +# ttl = "${ var.record_ttl }" +# domain = "${ var.domain }" +# } diff --git a/packet/modules/dns/input.tf b/packet/modules/dns/input.tf new file mode 100644 index 0000000..4464c7f --- /dev/null +++ b/packet/modules/dns/input.tf @@ -0,0 +1,5 @@ +variable "name" {} +variable "master_ips" { type = "list" } +variable "master_node_count" {} +variable "domain" {} +variable "record_ttl" { default = "60" } diff --git a/packet/modules/dns/output.tf b/packet/modules/dns/output.tf new file mode 100644 index 0000000..e69de29 diff --git a/packet/modules/etcd/etcd-cloud-config.yml b/packet/modules/etcd/etcd-cloud-config.yml index 1385a59..e11af7c 100644 --- a/packet/modules/etcd/etcd-cloud-config.yml +++ b/packet/modules/etcd/etcd-cloud-config.yml @@ -4,13 +4,13 @@ coreos: etcd2: - advertise-cliadvertise-client-urls: http://$private_ipv4:2379 + advertise-client-urls: http://${ fqdn }:2379 # cert-file: /etc/kubernetes/ssl/k8s-etcd.pem debug: true - initial-advertise-peer-urls: https://$private_ipv4:2380 + initial-advertise-peer-urls: https://${ fqdn }:2380 # key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem - listen-client-urls: http://$private_ipv4:2379,http://127.0.0.1:2379 - listen-peer-urls: https://$private_ipv4:2380 + listen-client-urls: http://${ fqdn }:2379,http://127.0.0.1:2379 + listen-peer-urls: https://${ fqdn }:2380 name: ${ hostname } discovery: ${ etcd_discovery } peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem diff --git a/packet/ssl-ssh-cloud.tf b/packet/ssl-ssh-cloud.tf index 49efa32..54f4c17 100644 --- a/packet/ssl-ssh-cloud.tf +++ b/packet/ssl-ssh-cloud.tf @@ -5,8 +5,8 @@ resource "null_resource" "ssl_ssh_gen" { command = < Date: Thu, 20 Apr 2017 10:31:47 +1200 Subject: [PATCH 058/149] Fix ETCD2 Cluster Name so ETCD can Bootstrap --- entrypoint.sh | 2 +- packet/modules/etcd/etcd-cloud-config.tf | 1 - packet/modules/etcd/etcd-cloud-config.yml | 2 +- packet/packet.tf | 6 ++++++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 5586a7e..a0577da 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -12,7 +12,7 @@ export TF_VAR_data_dir=/cncf/data/${TF_VAR_name} # tfstate, sslcerts, and ssh keys are currently stored in TF_VAR_data_dir mkdir -p $TF_VAR_data_dir cd $TF_VAR_data_dir - + # Run CMD if [ "$1" = "aws-deploy" ] ; then terraform get /build/aws && \ diff --git a/packet/modules/etcd/etcd-cloud-config.tf b/packet/modules/etcd/etcd-cloud-config.tf index c1ff25e..34172f4 100644 --- a/packet/modules/etcd/etcd-cloud-config.tf +++ b/packet/modules/etcd/etcd-cloud-config.tf @@ -43,7 +43,6 @@ data "template_file" "etcd_user_data" { vars { fqdn = "etcd${ count.index + 1 }.${ var.internal_tld }" - hostname = "etcd${ count.index + 1 }" cluster_domain = "${ var.cluster_domain }" dns_service_ip = "${ var.dns_service_ip }" kubelet_image_url = "${ var.kubelet_image_url }" diff --git a/packet/modules/etcd/etcd-cloud-config.yml b/packet/modules/etcd/etcd-cloud-config.yml index e11af7c..46fe1fb 100644 --- a/packet/modules/etcd/etcd-cloud-config.yml +++ b/packet/modules/etcd/etcd-cloud-config.yml @@ -11,7 +11,7 @@ coreos: # key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem listen-client-urls: http://${ fqdn }:2379,http://127.0.0.1:2379 listen-peer-urls: https://${ fqdn }:2380 - name: ${ hostname } + name: ${ fqdn } discovery: ${ etcd_discovery } peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem peer-client-cert-auth: true diff --git a/packet/packet.tf b/packet/packet.tf index a438b9c..421a20a 100644 --- a/packet/packet.tf +++ b/packet/packet.tf @@ -11,3 +11,9 @@ resource "packet_ssh_key" "cncf" { name = "${ var.name }" public_key = "${file("${ var.data_dir }/.ssh/id_rsa.pub")}" } + + # terraform { +# backend "local" { +# path = "${ var.data_dir}/terraform.tfstate" +# } +# } From 006b750f34fd95072289c3fe9e150e31e7d0b669 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 12:00:15 +1200 Subject: [PATCH 059/149] Update Certs to use Pubilc Endpoint Name --- packet/init-cfssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packet/init-cfssl b/packet/init-cfssl index 9bd9e49..ad4ea21 100755 --- a/packet/init-cfssl +++ b/packet/init-cfssl @@ -97,10 +97,10 @@ echo "$(ca-config)" >ca-config.json # generate ca cfssl gencert -initca ca-csr.json | cfssljson -bare ca - _chmod ca - + # generate keys and certs generate k8s-admin client-server "${DEFAULT_HOSTS}" -generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},master.${INTERNAL_TLD},*.${REGION}.cloudapp.azure.com" +generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},master.${INTERNAL_TLD},endpoint.${INTERNAL_TLD}" generate k8s-etcd client-server "etcd.${INTERNAL_TLD},etcd1.${INTERNAL_TLD},etcd2.${INTERNAL_TLD},etcd3.${INTERNAL_TLD}" generate k8s-worker client "${DEFAULT_HOSTS}" From b08953ae3cafb20d6373db88d33c4cea7d008cc1 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 12:01:07 +1200 Subject: [PATCH 060/149] Generate Kubeconfig ENV Per Cluster Deploy --- packet/modules/kubeconfig/input.tf | 6 ++++++ packet/modules/kubeconfig/kubeconfig.tf | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 packet/modules/kubeconfig/input.tf create mode 100644 packet/modules/kubeconfig/kubeconfig.tf diff --git a/packet/modules/kubeconfig/input.tf b/packet/modules/kubeconfig/input.tf new file mode 100644 index 0000000..19ab85c --- /dev/null +++ b/packet/modules/kubeconfig/input.tf @@ -0,0 +1,6 @@ +variable "admin_key_pem" {} +variable "admin_pem" {} +variable "ca_pem" {} +variable "fqdn_k8s" {} +variable "name" {} +variable "data_dir" {} diff --git a/packet/modules/kubeconfig/kubeconfig.tf b/packet/modules/kubeconfig/kubeconfig.tf new file mode 100644 index 0000000..24f41b9 --- /dev/null +++ b/packet/modules/kubeconfig/kubeconfig.tf @@ -0,0 +1,23 @@ +resource "null_resource" "kubeconfig" { + + provisioner "local-exec" { + command = < Date: Thu, 20 Apr 2017 12:04:47 +1200 Subject: [PATCH 061/149] Create Public Endpoint DNS --- packet/modules.tf | 21 ++++++++++++--------- packet/modules/dns/dns.tf | 27 +++++++++++++++++++++++++++ packet/modules/dns/input.tf | 3 +++ packet/output.tf | 14 +++++++------- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/packet/modules.tf b/packet/modules.tf index abb9d8a..effd0b8 100644 --- a/packet/modules.tf +++ b/packet/modules.tf @@ -10,7 +10,10 @@ module "dns" { source = "./modules/dns" name = "${ var.name }" master_ips = "${ module.etcd.master_ips }" + public_master_ips = "${ module.etcd.public_master_ips }" + public_worker_ips = "${ module.worker.public_worker_ips }" master_node_count = "${ var.master_node_count }" + worker_node_count = "${ var.worker_node_count }" domain = "${ var.domain }" } @@ -79,13 +82,13 @@ module "worker" { data_dir = "${ var.data_dir }" } -# module "kubeconfig" { -# source = "./modules/kubeconfig" +module "kubeconfig" { + source = "./modules/kubeconfig" -# admin_key_pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" -# admin_pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" -# ca_pem = "${ var.data_dir }/.cfssl/ca.pem" -# data_dir = "${ var.data_dir }" -# fqdn_k8s = "${ module.etcd.fqdn_lb }" -# name = "${ var.name }" -# } + admin_key_pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" + admin_pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" + ca_pem = "${ var.data_dir }/.cfssl/ca.pem" + data_dir = "${ var.data_dir }" + fqdn_k8s = "endpoint.${ var.name }.${ var.domain }" + name = "${ var.name }" +} diff --git a/packet/modules/dns/dns.tf b/packet/modules/dns/dns.tf index 266d6f0..fe57011 100644 --- a/packet/modules/dns/dns.tf +++ b/packet/modules/dns/dns.tf @@ -41,6 +41,33 @@ resource "dnsimple_record" "A-master" { domain = "${ var.domain }" } +resource "dnsimple_record" "A-public-endpoint" { + count = "${ var.master_node_count }" + name = "endpoint.${ var.name }" + value = "${ element(var.public_master_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + +resource "dnsimple_record" "A-public-masters" { + count = "${ var.master_node_count }" + name = "master${ count.index + 1 }.${ var.name }" + value = "${ element(var.public_master_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + +resource "dnsimple_record" "A-public-workers" { + count = "${ var.worker_node_count }" + name = "worker${ count.index + 1 }.${ var.name }" + value = "${ element(var.public_worker_ips, count.index) }" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} + # resource "dnsimple_record" "etcd-client-tcp" { # count = "${ var.master_node_count }" # name = "_etcd-client._tcp.${ var.name }" diff --git a/packet/modules/dns/input.tf b/packet/modules/dns/input.tf index 4464c7f..5cf4803 100644 --- a/packet/modules/dns/input.tf +++ b/packet/modules/dns/input.tf @@ -1,5 +1,8 @@ variable "name" {} variable "master_ips" { type = "list" } +variable "public_master_ips" { type = "list" } +variable "public_worker_ips" { type = "list" } variable "master_node_count" {} +variable "worker_node_count" {} variable "domain" {} variable "record_ttl" { default = "60" } diff --git a/packet/output.tf b/packet/output.tf index 6daf46b..402acbf 100644 --- a/packet/output.tf +++ b/packet/output.tf @@ -4,10 +4,10 @@ # output "k8s_admin" { value = "${ k8s_admin}"} # # fixme for use outside container output "ssh_key_setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir }/.ssh/id_rsa"} -output "ssh_first_master" { value = "ssh -At core@${ module.etcd.first_master_ip }" } -output "ssh_second_master" { value = "ssh -At core@${ module.etcd.second_master_ip }" } -output "ssh_third_master" { value = "ssh -At core@${ module.etcd.third_master_ip }" } -output "ssh_first_worker" { value = "ssh -At core@${ module.worker.first_worker_ip }" } -output "ssh_second_worker" { value = "ssh -At core@${ module.worker.second_worker_ip }" } -output "ssh_third_worker" { value = "ssh -At core@${ module.worker.third_worker_ip }" } -# " ssh ${ var.admin_username }@etcd1.${ var.internal_tld }"} +output "ssh_first_master" { value = "ssh -At core@master1.${ var.name }.${ var.domain }" } +output "ssh_second_master" { value = "ssh -At core@master2.${ var.name }.${ var.domain }" } +output "ssh_third_master" { value = "ssh -At core@master3.${ var.name }.${ var.domain }" } +output "ssh_first_worker" { value = "ssh -At core@worker1.${ var.name }.${ var.domain }" } +output "ssh_second_worker" { value = "ssh -At core@worker2.${ var.name }.${ var.domain }" } +output "ssh_third_worker" { value = "ssh -At core@worker3.${ var.name }.${ var.domain }" } +# " ssh ${ var.admin_username }@etcd1.${ var.name }.${ var.domain }"} From 5404a1e145979b774d474726f27dd2fda76d44b9 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 12:05:32 +1200 Subject: [PATCH 062/149] Update ETCD Output --- packet/modules/etcd/output.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/packet/modules/etcd/output.tf b/packet/modules/etcd/output.tf index a4c4e9c..245be33 100644 --- a/packet/modules/etcd/output.tf +++ b/packet/modules/etcd/output.tf @@ -3,3 +3,4 @@ output "first_master_ip" { value = "${ packet_device.masters.0.network.0.address output "second_master_ip" { value = "${ packet_device.masters.1.network.0.address }" } output "third_master_ip" { value = "${ packet_device.masters.2.network.0.address }" } output "master_ips" { value = ["${ packet_device.masters.*.network.2.address }"] } +output "public_master_ips" { value = ["${ packet_device.masters.*.network.0.address }"] } From b403f0536ea5998429ecb0a39e31f65d0ba0ee20 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 12:06:11 +1200 Subject: [PATCH 063/149] Remove Cloud-provider=azure --- packet/modules/worker/output.tf | 5 +---- packet/modules/worker/worker-cloud-config.yml | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packet/modules/worker/output.tf b/packet/modules/worker/output.tf index 4f0e4a7..9eb93eb 100644 --- a/packet/modules/worker/output.tf +++ b/packet/modules/worker/output.tf @@ -1,5 +1,2 @@ #output "fqdn_lb" { value = "${azurerm_public_ip.cncf.fqdn}" } -output "first_worker_ip" { value = "${ packet_device.workers.0.network.0.address }" } -output "second_worker_ip" { value = "${ packet_device.workers.1.network.0.address }" } -output "third_worker_ip" { value = "${ packet_device.workers.2.network.0.address }" } -output "worker_ips" { value = ["${ packet_device.workers.*.network.0.address }"] } +output "public_worker_ips" { value = ["${ packet_device.workers.*.network.0.address }"] } diff --git a/packet/modules/worker/worker-cloud-config.yml b/packet/modules/worker/worker-cloud-config.yml index 4a160b7..6ae9411 100644 --- a/packet/modules/worker/worker-cloud-config.yml +++ b/packet/modules/worker/worker-cloud-config.yml @@ -43,8 +43,6 @@ coreos: ExecStart=/usr/lib/coreos/kubelet-wrapper \ --allow-privileged=true \ --api-servers=http://master.${ internal_tld }:8080 \ - --cloud-provider=azure \ - --cloud-config=/etc/kubernetes/ssl/azure-config.json \ --cluster-dns=${ dns_service_ip } \ --cluster_domain=${ cluster_domain } \ --config=/etc/kubernetes/manifests \ From f42f8329244967253de8720f1e89ce0f7509c8f1 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Thu, 20 Apr 2017 15:52:00 +1200 Subject: [PATCH 064/149] Clear up AWS dependency loop for cross-cloud deploy --- aws/modules.tf | 17 ++++++++--------- aws/modules/bastion/ec2.tf | 1 - aws/modules/bastion/input.tf | 1 - aws/modules/etcd/ec2.tf | 1 - aws/modules/etcd/output.tf | 2 +- aws/modules/vpc/public.tf | 2 -- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/aws/modules.tf b/aws/modules.tf index e871060..c9c4da0 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -50,19 +50,18 @@ module "etcd" { pod_cidr = "${ var.pod_cidr }" region = "${ var.aws_region }" service_cidr = "${ var.service_cidr }" - subnet_ids_private = "${ module.vpc.subnet_ids_private }" - subnet_ids_public = "${ module.vpc.subnet_ids_public }" - vpc_id = "${ module.vpc.id }" - ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" - k8s_etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" - k8s_etcd_key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" - k8s_apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" - k8s_apiserver_key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" + subnet_ids_private = "${ module.vpc.subnet_ids_private }" + subnet_ids_public = "${ module.vpc.subnet_ids_public }" + vpc_id = "${ module.vpc.id }" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s_etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" + k8s_etcd_key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" + k8s_apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" + k8s_apiserver_key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" } module "bastion" { source = "./modules/bastion" - depends_id = "${ module.etcd.depends_id }" ami_id = "${ var.aws_image_ami }" instance_type = "${ var.aws_bastion_vm_size }" diff --git a/aws/modules/bastion/ec2.tf b/aws/modules/bastion/ec2.tf index 013f739..a1697fc 100644 --- a/aws/modules/bastion/ec2.tf +++ b/aws/modules/bastion/ec2.tf @@ -12,7 +12,6 @@ resource "aws_instance" "bastion" { tags { builtWith = "terraform" kz8s = "${ var.name }" - depends-id = "${ var.depends_id }" Name = "kz8s-bastion" role = "bastion" } diff --git a/aws/modules/bastion/input.tf b/aws/modules/bastion/input.tf index c1ced18..d06cfe6 100644 --- a/aws/modules/bastion/input.tf +++ b/aws/modules/bastion/input.tf @@ -1,5 +1,4 @@ variable "ami_id" {} -variable "depends_id" {} variable "instance_type" {} variable "internal_tld" {} variable "key_name" {} diff --git a/aws/modules/etcd/ec2.tf b/aws/modules/etcd/ec2.tf index 3149048..9240b41 100644 --- a/aws/modules/etcd/ec2.tf +++ b/aws/modules/etcd/ec2.tf @@ -17,7 +17,6 @@ resource "aws_instance" "etcd" { tags { builtWith = "terraform" - depends-id = "${ var.depends_id }" KubernetesCluster = "${ var.name }" # used by kubelet's aws provider to determine cluster kz8s = "${ var.name }" Name = "etcd${ count.index + 1 }-${ var.name }" diff --git a/aws/modules/etcd/output.tf b/aws/modules/etcd/output.tf index 5bcb0c8..ffd51a1 100644 --- a/aws/modules/etcd/output.tf +++ b/aws/modules/etcd/output.tf @@ -1,5 +1,5 @@ -output "depends_id" { value = "${ null_resource.dummy_dependency.id }" } +#output "depends_id" { value = "${ null_resource.dummy_dependency.id }" } output "external_elb" { value = "${ aws_elb.external.dns_name }" } output "internal_ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } diff --git a/aws/modules/vpc/public.tf b/aws/modules/vpc/public.tf index 5d7b1af..609a03c 100644 --- a/aws/modules/vpc/public.tf +++ b/aws/modules/vpc/public.tf @@ -6,7 +6,6 @@ resource "aws_internet_gateway" "main" { KubernetesCluster = "${ var.name }" kz8s = "${ var.name }" Name = "kz8s-${ var.name }" - version = "${ var.kubelet_image_tag }" } } @@ -23,7 +22,6 @@ resource "aws_subnet" "public" { KubernetesCluster = "${ var.name }" kz8s = "${ var.name }" Name = "kz8s-${ var.name }-public" - version = "${ var.kubelet_image_tag }" visibility = "public" } } From cf95b02306260700658e4e075c17feba5bdad7e6 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Thu, 20 Apr 2017 15:53:58 +1200 Subject: [PATCH 065/149] terraform aws_iam_role 'roles' parameted deprecation --- aws/modules/iam/etcd.tf | 4 +--- aws/modules/iam/worker.tf | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/aws/modules/iam/etcd.tf b/aws/modules/iam/etcd.tf index b5c3046..03673b1 100644 --- a/aws/modules/iam/etcd.tf +++ b/aws/modules/iam/etcd.tf @@ -18,9 +18,7 @@ EOS resource "aws_iam_instance_profile" "master" { name = "master-k8s-${ var.name }" - roles = [ - "${ aws_iam_role.master.name }" - ] + role = "${ aws_iam_role.master.name }" } resource "aws_iam_role_policy" "master" { diff --git a/aws/modules/iam/worker.tf b/aws/modules/iam/worker.tf index 11227e5..ea0b9cd 100644 --- a/aws/modules/iam/worker.tf +++ b/aws/modules/iam/worker.tf @@ -20,7 +20,7 @@ EOS resource "aws_iam_instance_profile" "worker" { name = "worker-k8s-${ var.name }" - roles = [ "${ aws_iam_role.worker.name }" ] + role = "${ aws_iam_role.worker.name }" } resource "aws_iam_role_policy" "worker" { From c1d5d9c3222091a2f350f8b7d9e5d8b2efb19f6b Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Thu, 20 Apr 2017 15:55:12 +1200 Subject: [PATCH 066/149] aws master_node_count and master_ips needed for dns --- aws/modules.tf | 23 ++++++++++++----------- aws/modules/dns/input.tf | 2 +- azure/azure.tf | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/aws/modules.tf b/aws/modules.tf index c9c4da0..6026f03 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -23,25 +23,26 @@ module "iam" { module "dns" { - source = "./modules/dns" - name = "${ var.name }" - - master_ips = "${ module.etcd.master_ips }" - internal_tld = "${ var.internal_tld }" - vpc_id = "${ module.vpc.id }" + source = "./modules/dns" + name = "${ var.name }" + master_node_count = "${ var.master_node_count }" + master_ips = "${ module.etcd.master_ips }" + internal_tld = "${ var.internal_tld }" + vpc_id = "${ module.vpc.id }" } module "etcd" { - source = "./modules/etcd" - #depends_id = "${ module.dns.depends_id }" - instance_profile_name = "${ module.iam.instance_profile_name_master }" + source = "./modules/etcd" + depends_id = "${ module.dns.depends_id }" + instance_profile_name = "${ module.iam.instance_profile_name_master }" + master_node_count = "${ var.master_node_count }" name = "${ var.name }" ami_id = "${ var.aws_image_ami }" key_name = "${ var.aws_key_name }" cluster_domain = "${ var.cluster_domain }" - kubelet_image_url = "${ var.kubelet_image_url }" - kubelet_image_tag = "${ var.kubelet_image_tag }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" dns_service_ip = "${ var.dns_service_ip }" etcd_security_group_id = "${ module.security.etcd_id }" external_elb_security_group_id = "${ module.security.external_elb_id }" diff --git a/aws/modules/dns/input.tf b/aws/modules/dns/input.tf index 23d1b0a..4c2eeca 100644 --- a/aws/modules/dns/input.tf +++ b/aws/modules/dns/input.tf @@ -1,6 +1,6 @@ variable "internal_tld" {} variable "name" {} variable "vpc_id" {} -variable "master_ips" {} +variable "master_ips" { type = "list"} variable "master_node_count" {} diff --git a/azure/azure.tf b/azure/azure.tf index 74661bf..78f6c81 100644 --- a/azure/azure.tf +++ b/azure/azure.tf @@ -10,7 +10,7 @@ resource "azurerm_storage_account" "cncf" { # * azurerm_storage_account.cncf: name can only consist of lowercase letters # and numbers, and must be between 3 and 24 characters long FIXME: # storage_account name must be globally unique - name = "${ var.name }cncfdemo" + name = "${ var.name }x" resource_group_name = "${ var.name }" location = "${ var.location }" account_type = "Standard_LRS" From 141ff4b188a639ccebf23d16af4bad04ed800c9f Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Thu, 20 Apr 2017 15:55:56 +1200 Subject: [PATCH 067/149] simultaneous cross-cloud deploy to aws,azure,packet --- entrypoint.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index a0577da..0061397 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -50,4 +50,20 @@ elif [ "$1" = "packet-deploy" ] ; then printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "packet-destroy" ] ; then time terraform destroy -force /build/packet +elif [ "$1" = "cross-cloud-deploy" ] ; then + terraform get /build/cross-cloud && \ + terraform apply -target module.aws.null_resource.ssl_gen /build/cross-cloud && \ + terraform apply -target module.azure.azurerm_resource_group.cncf /build/cross-cloud && \ + terraform apply -target module.azure.null_resource.ssl_ssh_cloud_gen /build/cross-cloud && \ + terraform apply -target module.azure.module.dns.null_resource.dns_gen /build/cross-cloud && \ + terraform apply -target module.etcd.azurerm_network_interface.cncf /build/cross-cloud && \ + terraform apply -target module.packet.null_resource.ssl_ssh_gen /build/cross-cloud && \ + terraform apply -target module.packet.module.etcd.null_resource.discovery_gen /build/cross-cloud && \ + time terraform apply /build/cross-cloud && \ + printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ + printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ + printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} +elif [ "$1" = "cross-cloud-destroy" ] ; then + time terraform destroy -force /build/cross-cloud fi + From 96ded117f800c3bf308c4403da5ee58887fc3f20 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 6 Apr 2017 08:09:03 +1200 Subject: [PATCH 068/149] Refactor Azure to Gce --- azure.tf | 32 ++++ cert.tf | 27 +++ cloud-config.tf | 37 ++++ docs/azure_app_endpoints.png | Bin 0 -> 33939 bytes docs/azure_app_registration.png | Bin 0 -> 47889 bytes docs/guid_from_oauth_endpoint.png | Bin 0 -> 51214 bytes docs/key_generation_copy_me.png | Bin 0 -> 68431 bytes docs/research.md | 61 +++++++ docs/research.org | 281 ++++++++++++++++++++++++++++++ docs/web_api_application_type.png | Bin 0 -> 34235 bytes init-cfssl | 109 ++++++++++++ input.tf | 24 +++ keypair.tf | 22 +++ modules.tf | 111 ++++++++++++ modules/bastion/input.tf | 17 ++ modules/bastion/node.tf | 66 +++++++ modules/bastion/output.tf | 2 + modules/bastion/user-data.yml | 26 +++ modules/dns/dns.tf | 136 +++++++++++++++ modules/dns/input.tf | 6 + modules/dns/output.tf | 4 + modules/etcd/.#cloud-config.tf | 1 + modules/etcd/cloud-config.tf | 24 +++ modules/etcd/cloud-config.yml | 280 +++++++++++++++++++++++++++++ modules/etcd/input.tf | 26 +++ modules/etcd/load-balancer.tf | 47 +++++ modules/etcd/nodes.tf | 53 ++++++ modules/etcd/output.tf | 8 + modules/kubeconfig/io.tf | 8 + modules/kubeconfig/kubeconfig.tf | 50 ++++++ modules/security/io.tf | 9 + modules/security/security.tf | 123 +++++++++++++ modules/vpc/azure-security.tf | 6 + modules/vpc/azure-subnet.tf | 61 +++++++ modules/vpc/io.tf | 16 ++ modules/vpc/private.tf | 54 ++++++ modules/vpc/public.tf | 44 +++++ modules/vpc/vpc.tf | 43 +++++ modules/worker/cloud-config.tf | 14 ++ modules/worker/cloud-config.yml | 158 +++++++++++++++++ modules/worker/input.tf | 22 +++ modules/worker/nodes.tf | 105 +++++++++++ output.tf | 22 +++ readme.org | 211 ++++++++++++++++++++++ runme | 12 ++ wait-for-cluster | 18 ++ 46 files changed, 2376 insertions(+) create mode 100644 azure.tf create mode 100644 cert.tf create mode 100644 cloud-config.tf create mode 100644 docs/azure_app_endpoints.png create mode 100644 docs/azure_app_registration.png create mode 100644 docs/guid_from_oauth_endpoint.png create mode 100644 docs/key_generation_copy_me.png create mode 100644 docs/research.md create mode 100644 docs/research.org create mode 100644 docs/web_api_application_type.png create mode 100755 init-cfssl create mode 100644 input.tf create mode 100644 keypair.tf create mode 100644 modules.tf create mode 100644 modules/bastion/input.tf create mode 100644 modules/bastion/node.tf create mode 100644 modules/bastion/output.tf create mode 100644 modules/bastion/user-data.yml create mode 100644 modules/dns/dns.tf create mode 100644 modules/dns/input.tf create mode 100644 modules/dns/output.tf create mode 120000 modules/etcd/.#cloud-config.tf create mode 100644 modules/etcd/cloud-config.tf create mode 100644 modules/etcd/cloud-config.yml create mode 100644 modules/etcd/input.tf create mode 100644 modules/etcd/load-balancer.tf create mode 100644 modules/etcd/nodes.tf create mode 100644 modules/etcd/output.tf create mode 100644 modules/kubeconfig/io.tf create mode 100644 modules/kubeconfig/kubeconfig.tf create mode 100644 modules/security/io.tf create mode 100644 modules/security/security.tf create mode 100644 modules/vpc/azure-security.tf create mode 100644 modules/vpc/azure-subnet.tf create mode 100644 modules/vpc/io.tf create mode 100644 modules/vpc/private.tf create mode 100644 modules/vpc/public.tf create mode 100644 modules/vpc/vpc.tf create mode 100644 modules/worker/cloud-config.tf create mode 100644 modules/worker/cloud-config.yml create mode 100644 modules/worker/input.tf create mode 100644 modules/worker/nodes.tf create mode 100644 output.tf create mode 100644 readme.org create mode 100755 runme create mode 100755 wait-for-cluster diff --git a/azure.tf b/azure.tf new file mode 100644 index 0000000..2623b2d --- /dev/null +++ b/azure.tf @@ -0,0 +1,32 @@ +# Configure the Microsoft Azure Provider +provider "azurerm" { } + +resource "azurerm_resource_group" "cncf" { + name = "${ var.name }" + location = "${ var.location }" +} + +resource "azurerm_storage_account" "cncf" { + # * azurerm_storage_account.cncf: name can only consist of lowercase letters + # and numbers, and must be between 3 and 24 characters long FIXME: + # storage_account name must be globally unique + name = "${ var.name }cncfdemo" + resource_group_name = "${ var.name }" + location = "${ var.location }" + account_type = "Standard_LRS" +} + +resource "azurerm_storage_container" "cncf" { + name = "${ var.name }" + resource_group_name = "${ var.name }" + storage_account_name = "${ azurerm_storage_account.cncf.name }" + container_access_type = "private" +} + +resource "azurerm_availability_set" "cncf" { + name = "${ var.name }" + resource_group_name = "${ var.name }" + location = "${ var.location }" + +} + diff --git a/cert.tf b/cert.tf new file mode 100644 index 0000000..49eeb10 --- /dev/null +++ b/cert.tf @@ -0,0 +1,27 @@ +#Gen Certs +resource "null_resource" "ssl_gen" { + + provisioner "local-exec" { + command = < ${ var.data-dir }/azure-config.json +{ + "aadClientId": "$${ARM_CLIENT_ID}", + "aadClientSecret": "$${ARM_CLIENT_SECRET}", + "tenantId": "$${ARM_TENANT_ID}", + "subscriptionId": "$${ARM_SUBSCRIPTION_ID}", + "resourceGroup": "${ var.name }", + "location": "${ var.location }", + "subnetName": "${ var.name }", + "securityGroupName": "${ var.name }", + "vnetName": "${ var.name }", + "routeTableName": "${ var.name }", + "primaryAvailabilitySetName": "${ var.name }" +} +JSON +COMMAND + } + + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = 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 XOe+`iNJHNs@K07!QKCfb!{`43_p$bc literal 0 HcmV?d00001 diff --git a/docs/azure_app_registration.png b/docs/azure_app_registration.png new file mode 100644 index 0000000000000000000000000000000000000000..7701d57f25cf1baca9f50585afb805dde8cac2d5 GIT binary patch literal 47889 zcmb@ubyStnw=QgFaSYB2f;SJ84Cr_RrNJ@w(K6&!I?a7m;4Y1F^FM0A~-r&FI zPC}B(u&}Um%W}V;JRy1_DI%!sp1QZ-qK>YG`{bxfTEd?Q;myl8Z(~CQgFizc9uvDY zjk_U+9~ZuSqH`s|mx5BFQcL7l6E&AhoZd6}uFi)WBp}CKUS3`8rE@tA@tB#q>6s1> zD4Mh4{23O!?89akp8orIl(M{k@WdA*JUm=OLjwi|27Fk+hx>c*Dn}6UUPo0GGffs+ zS-G;gd3d1y_rMPZgbhb;HFr;@Zf+ z!R%i-njbCP-(EKIy!i9W$ED}um+Rlgw)5mN#9+C&xm^vHZZ8k>Cs zx%pUGS-H5l7#NU@L z2eiXkoS-neTe0kZSR_MejXaP8@l5#!9*ACAOpFo_rppTT!Zd9lzxS2>$y)D0_%d|N zr2~fNvs3Gk^c6Jp={>W-$DxUC4|Cp6s)~xUbq+?G7tIc<-AZ|~1{ThZ!(u(%-3|5i z#E`S|a~T<#xVShJAu!{%wl*-4>8v3~#sRvVt?!R_Xt0^<1mcefoPN$HJM2@1cDByJ zK6ga#IP)s=v5=PPd^^;_YFQK}wmW|`Bm|vyCi4mIrMt#|{`?FHDRQDnt;%~=5dBRK z&4b7t3nP+?lG506N1$4<`Y-UHt)@yXtgLpooKKI#UTv?fWoKts8~36HyiqID9ULBh z#Nh9K_WZfoK;oxnCGgxG-YRNqYs<@zbOxiHpPzGSKsfsQ`)O!tX(=c;tfv{lI!;=j zW3L!YF$aMS;$84GaTC9tE<=fTd=0d`rTbI z37J3KT^n_Wwe^E%hefL{uc{iKlr$6kbV<^apm9Y};58RyvD;V-+YN4i3&<`sdOP-f^Jz&z?Q=xjD12wgwBUhC&ZZ zr|mn5c6W9vtEyC#m5*{}W@cPmT&~6r2D5?s#l^CjM7_0G<6v$Mxvqa`emShJ+0 zq*z&6o}8W<85yCWp_vV*5BB$O%UM-e)eoX{GvaZPrHtf8>@lA>Twy#E8wPeb9B5yC zWMyMhQC02k?Bw;myUOr;*dCSr# zeP29uvhofX15#3%eXx(B1WlKe1dd%`pq>A4C+~xNG$}oiHTV0=pKB9}pADz+kOGnj zuEu~qf=hlG@#EM~AI70~I-h{cw1C+7bOeYASM5Q3^Ah2W8ukuLe=c z$=qHS)*E@~Z@MNX&aaMFf$sJ7^)hA=13Ug@&2 zefR>06@kyA3VfdX(QoMldJ_|q{W&OufPkKy-0(Zh8rjt7$(-% zcWb?9dsN5ON}isc>gv-|WqP6_B4J@+D2?+vz54;>4(3TcmF)`EmItSrE#IBxC9vH=JW@cu*?7XPaayMC7B)-1B zbaW+P9pEz%kSwNwySsa5*PqwK#KhrzJsK{qmA?LuQc^@78z%@;XPVF`&MEzmfX|%F zU>;gfTRyjg`COTltE;Q>lkV{oaC`it9SHjbz)hJMq?x2PBx zI<3BZ4-K~F=A6JjXKSpu@XTBVxw*JxQrPLZxf`;xMc%*H|MBrDxKX#R`9PkIMtact z`uhG@-f*^LjBGk@Z+U)^Ui%e&%-63KRaH(dE?rLM(PF5ms3fGM7OXgvH6SpwD3!mW z)hnTO!|Tm=^o@#whiOdkkBNz?sbTBCl#3Q6I~4j>R56t4C;KdXTLLF7HWq@+%*JLy zdDzJ{!s#<*5=5ne=ValrcOtz;(-$J)OWFHNZPHj5J>}9?Z!26$byxf+!kwZ{?XN>x zw&aEheMBKjA{N`U;cX`+cI^Bx%2Fx;JYU@Y%--v~o|g>VMYQz7i|)60p-pb{hT-We zQ9C!9Jy;2~SM~FW8$l+$YffAd;){*etuk7=`!WXI%#oxgS&F&O&ya;4d1pARraMDx zwIJ6o6hrva!+bw(j=&#ofBEu-mzTG)vXVA~fZO>eP+2v#%k$l-)6L;Ok(`A^7#0|V z_2a|6t*tGoc!c-)&cxmI>H6B*(WRlC-67DL7N1*^+$PuVFl-PT>WDHe*O!M&m#3#nAbhs^wdUlkA1!w@H#L>#?M{}QUR;;TPEQ&VeN0w=EWz@hL%fE@St_c74X-@w6T@OhC_Q*-P8cw+b?l7KIn*W+ZoKnXZz zVIiTV?CR=z)o?{!55h4qv8t--$;k=O;Fgva^fxfWFxU}+nZki$ zA|mKmSgQtVDk>J1mdwWPu$f_D@OE}~xVX3&7;(wT$>rsY-ilJ5o;NF9p}+=Ege)v9 zP*6}h!%i=bp#?oYiiavHyNm@LqbB@`J1OpR?hscMjRZYX_=TD)_#74 zCC0~3>+BSPgM*XBWcsF9Ku%7+xVVV7F+xV?#M}^bjmXUkiN~WcG-#m_`RcX$=187wTHo-nI4oGF-C|SH0&w1asY?Y$ zK%vxA$jVuhPuVM8Ch0B1VbX4$;W{+YJalSb)ovN3&=9ceaqYIz3uBZp`2=oLBTg#e zwF_TAxb3u+YhhW}Zc)OE!6HN{H8D3cF2(SkEyQtOK`(#s$z0@SDg&z&V)>rE<9+5ycA=3i!@tK!)bEoiyp$zZGZ^+^=5FtX5GtqqUk z+D{Paj`9Fn>A|HytVvBx1wd3sXW`d(ap2+^=;?j8M{_`+Sm8qG9vwyHamvlgGBhv% zGH#0!F4L|@_4?7%V+O+D!=1;Tg|Tt=moG353$Ks}^h4X*+pTSEfY~4mMMp>1*4A>^ zEp`e_yp4GE=8K`RF*-JOunGKh0Z;s?UIm%3j0}R6>&|$=6$wz$JtI_NbTl+03ya{i z6kr@R<>f&@ucI?7E13Zvq+L}|R!(KJQcWqcFRHJ<1|DXbn9q8;JQRx|IVtJ*=qQT8 zP*PU5w5h4dKaFHaEU`{WLX&g`W}v#dTDR3VoX}2EQgXV;|L*92=(NDgqn2`(+}YXL z=_lmx-YsLR{8eXwt>Y3B#9@z5Pvz2iujlKX$c#dbuEs<^e1Lz{g`1jSFo5Szc<%R{ z*n$k0-_;P9b+0#6)wN9Z%qqR4?#fx2ncJ6+$3{oX=W&dztv}o`{!kQ)2YSSAGYdqcp{V#7nM6`* zwm3CDzSeT0@Z-lPOiWDCV(MZ5n?hvbtnLK@%JPma)w2dZdY>K-p8z1k!dp^YOvWyR zX9*eUi6p!|+a{%_*Huzd($)r=`#!+8!h+z+NKQ^J;0i% zK_7K<;iK=0A6-(t|{;gqM|TD`PtdVv(*+salkXL6l0LA?4?CVqZ%?&Q&V4w zh2yaGO-!ih=rr3cwE#87!oaX`!mu?lHAN)gealn&0tN;^x|F1(V0;R)(COtPnQw#f zb9e?TvcwkQ##oGCcBjY3nZL{p4GmdwO7inzd2D-;kmA)pin_sb z7P;cpcb~N-!}4TMFMqG6_qaDx1wuP8zO||B80CkP_5Lty7Tcv38Ak+c_QNC-Wi~c8 zkZMjRk)iH48mAKn>L)2O!m}OwBlvl*{X~I`6si;fd2;T}c610!O7^z53yFvTWNh-s zeRK6L4I>~Rq_EoL8wa zdN#DSwtoGJa$19pADft%2%H~qDG99X?1mY2WTEOnJq#0@79(Vt)<_4+|H}cmdw8(1 zvPQ?mAi%?ef5GSS1rbO0@=n`-v8~k5G;n&V&S5p{`*+~QQe$ITEypPh*-jkDm+TQA zbqy~YR+pCn#ICKVFyQ~R0IYv+Kc}FepssFuZ0r=ko$2i-wr;G?5%o&en(hBuKa^pP z{T|{*`~z_puVK|~;J3E;hOU)`1qfuXUcIVHsXqqU$lBUkhP@uakPHr|W6Yt(UPWzF z@^E0{J5SnDP6FyV0TP~G*@dV~ZAJM#nr1~Y^=Dqm?Y_*fC0Elz1Ep+iZQ3aE8F@uv zyVhE2ly4uRBl={2MJcplA;j$qDAx)IQ8u3Rt50FjI&HBZ{zL)KNlpP438^RVb3sv& zxQNK~?CeVD^g^G1LISkD9^20efU`I#6dDu+D?~|79^idCqf%O03Kq@fxE54f%U;nE z8#@jn#Bmr}%Ghu;IF^^U8G=Fc^Z8qTZ+CNZ%G2QYLmV0S0h!(W)3RyYCRSE+t$u!P zZkNDMq$DOX7RSZJ_-9NE_4XPJmB77uvoVn5e_!))X$+s3jE1JaoW4auSC^lU5C3*0 zomV0?AmC*!PjPYaw9U8T;=Rq`j5$0^3yba4RJ=Q3dI}10P0i$KSoX-OtgQBl35?q_ z5B$Mu#-Vh+TVOXgH#g0m=Oh*Y z^pJCKV5R7HAY-8PtI>2sta>Z=BB90$|JwK)FN{(8_n!$oox8N94F`13PHcvtL7qPr zgW+XKSZHWyc({o!4lZt}j>dE|MJ8Y*&dz+M)jUOwq?J@X9>|xf;{99ovUbgxm$=q$ z7S=XRmdww(8%Bohau;v;=Uf_p325C9zcOtpwz#^eXwu)V>QJxWexeij15U57e=FQ& zc}7zDL2oE5`*Zt(f&=ERu^~~@)W{UdUWi1iclgW>#THSsbF*a^)UO)3* zcpB-*bU?8*%3exPtrQ{4dF1BiqAnR=r;|V?OLcGw2qJXmtAMiynXS>fR;{%L)K*7b z{cVdm03);$9KG<r#p(7?6A zVQi)hBc95u0S}_4W~6t^IPU_txw)B;mbNf55|x&=e|R{|OQ#|wMNLfnw2^_p_n1V; z9009{hlfs=V^IK`0h_xASI%+!>f zl{GdYp|P>Cs+iq%Pd(sAcO+pXU4zr+5QvVrfF8oZ0X`M*W3$>{B($~f#`5II@`@K* ze2Q{&1N`PmAfD$tIk~wP7yDY#qMew@DJd4LnwFN-t|~MX9HT4cCebM52k0l0)BDd{ zN&S6t{F|3o^$w%(2=)nH{S93mMo)-Eauk8WFDxv)Ld5%dXZF08)2VWK;3fQ9WMpJ{ zfb>R2j;>G5TmyZaoSY;jgMraEG;nuhWt^gRpB*e{si$LurAa!iQPe4*Qg4 zZR@YWg+t96vVrZkHfMZuqqEFXl0LhSSlESOoVFF+RBJbZ(RdyTRIfT(D^6D9t?-}G zhdJn|s>l(NEZ7ybrFIZaf524)9xy^2uw#0*0`JUmPW-LQo@ z+u?M+KkPss$P&Q`3A7p@vW%7KwWjg7RW|l+0r;$1s-2XQ0szAN?5yYhY|T6Acur2v zpTPMgQTA?B6@@KK#$Jt)O?y>`hW2-dBYX zoXiH;*nz1jkYlQV$PLUyCn~k0lIMB?)Aj zt{qjRSnv)i`UlxLZ^_HHEKzL7#-OJf-~Z?e#oFE7&B@96XRjd)UE(QHIU%8;fJv^1G^NR6bHlyUu!e_kMQ zorCdx_9lF?JI=tF8$Sbg@EV_m^V34X_f_byw3HN}6OCH*h2-V!L3G3OEm#J=#a=^E z>>NCt%LWH3ad&+CV}=LP-hD^)<_>(hC8mX_Lgj8>};}7c*l&WGwjG`(z?3oAj~GSS#d*WO2r(jq!ADOU;9?pmZ5e~xHyaEj#6D_OpcEJVKxA>oY|HqxE}2WueR{@n`)m!Qjeu`6 z1(d!X(yLhtDk>_#x@Tnxfm9bzDSCRX{!pj;>)*U*cCQ?_4v7L^{w=BC?X2-U{Hqup z_4woA{QdFC!F$Aed_qD!po(td{|Y?5&!dF>^78ZF;^QM&u>L*G98Fv4N?21<6T}O^ zBDlT!cOPGiDB*wgnjiRy{r^>ha{JGKA9?=!=wg0Q{z1gQGd?c87B%@D^*_E2tpA(L z|GeUVUvBz$Ufpx9fAe}W|LK1$1mWM&|Lp|+kGDHLb=+E+$a9qaGU_7Zrx`hKxxP-5 z(ZY18u0ND?bPxga_r`Y(vq{2cjoskAiGOC9b5dTURB6wkT2cUo7lKuw3K9}5iM=?# zLQ~eH&5}u_!`Yv4^LOA;Wk_kJn`3LSo{S%g_*aR$)8uw0XsCx;m(()#i^`ymI_oHn z&xR!YY_Q&fwy9sq$cuaKDW3hpge6|Sdf)fhL7%cJ|NE>?QmA3jMa%Q`L|8Iy6s8;@ zU;URdwW*gkx9)epOLM+(9k(&G{~RIWy}EA9eo;DklbTf9vAhJPR22Tht}rwJ^IN{YPV&Xh&G$9jIkf_N=|-cWnU?i`#iFond$d z%IHYhYS@s;9hseWN$=;y|-Q9W5L2bfA-^fXN*GySpAt8m@@p|)SEH?U}5ub6f>^qsx=7o*N zavVnz&ZqHNk$-V6!{;w&;Gr;s9OPfvOs&3hF|QPLVReUb#-I^INN7; zvDszypad6xySXX~o)ju9QoPjG43zTQs~venx>!6F0eg|=6XbvsOy4%k=RC_0EV^gy z_!f_5-)sF?3la7p(WF} zrgm0UKAQ&%vR?Z(HeiXLk!PD6Ix63V%BFL9{W?Ess(0ELPU#Y<#1bz)`J)WA*P^6N&h+nqEk!B$k+r5GO~VsP!S_{jyPhyZUeKj z(%6VT9+ZZfiYfyCyMSw0OLUf@__Hm`c|?VtK=db1-pfs9;%Sm~hblgXvm0Ovdknta z#WZlJ`2DKn?(sV4X86n;qDeJQJvIZwbGT6_vXKr?t?QMXX}UnP-@b3lMgMhamsFXr z`=)=RpDRzOQYY*5l8-Kd5C4TiY=@?o!RL|+sCu55kViPgGvUXyj*oz8N!qCP+BBIu zwXj1&5mKgh84GYZW3$K(x9&&`ItXYpAEkh0Hb`R14^O;1ZJmyda*4_N0 zk7IjP50Z0r&C!pe_~+iu^Mp%1eRd0VJDd9`7-#^)nJ^{oV#BfI$_x%OP}0$*-8S5B z|ECv#f4_$?5RB@&Z*M=_e5#`;B=Qq(@AgHsBZ;io$G||d%OU*U*9>U7-2;jIao6v0 z_$;$CrmCu;!Nd%ANp5e1v4SQXhU5vY;*G7-F86r-F3xV(AaL|%<^B-jKc1dXD$vm! z81Yy2CCz5ZL6YXyK0(n5K+67PDv!Z*OV!pl)Fv zFDTb}=kh4gTJv&gmw`u{-OsH|j!LRA_k4j_lJ_^}p!)_^;O=1iiu%1&(K|gg{5)2+ zUVN=lw_URBe#D@_z!(=5)i6bk<9jQgoc+mZr43ZqIYlFVxsp=k4?e-jsIu^xtm7)U z51Y!Yi{JV}6nr?6PvX(#cUmR&7TdPpvqZiWdKY?`@K~dArSff*o#q;iCb`QbWHRG1 zH*;7e1(sM)khrmcm_ZG|50o}>`+#KaGY`eDeP8{1%( zn3!U)Y#h)Lt8G}~^_D((neey#cl(W(pt3j6%&HPH2ZmfYLj%D&RLfvwU>;Nm#u5ZKC&3Z@tI86d@H= zKs;Y_Qy>C9z>+_QhdC7GE|UVyoSaT?$E#5~0%LjxJ_==iNU_{JSl(XW=kY1za|j5l zi8E1m?@T-vb6{ANBjCv@%o5Tant-KEeH^~?aKguP!%j)I$gg_4sYDTh>33h4!{gab z2G?nshurwSIKrvZk|H{6KRY;=VmNid?vqe|Kwo5IK>%wr~ zRDoOefp3zvW$HhDPB5&xXU8qE&->g-&$nL4$-vl&DrE4y!rp;xiQJA$3*jqePt+`8 z5j@hVUaNBAWy2W1(fnmDS1}?k!|X$|r)uC*NPZuV9DUUEQ%zN(qzL0Gk;KcEG;p_8 zOPV1w`Rhh|nV!66GK$c6gJx#-QL+Li-Q8!}-Gm`0>uA+jZ|BdC{S>9#BoShRoO3{_ zNh(@a7!HocN3Tv<$TCueczH2gi|WIdJeEF1k1FnIAAWp#U(x>bUIqVQ!SUd;Ro|Q& z*)BPGo`{H;QRLxwfc=#%tnOJ^9~tUc>S-gM5vyBoIUb zuH?A$HIovnu}g)e&KGjmmUH#o(jhT8@;J1g1%wWNoS&|lLEZ83tm-_G_ z%mc*r-WG8Jn8qzh2zLiUOKhY(*8!c0jhww*t#eHF2#jfE-gXp~s67zDHLvdg&?3p8h zKwhU^+?*j2kB_+7OEVA;%_%D?&mT7R%&kglE58+jy$whX4BT8h{=SW!G%$nf-!#vA zSCN(Vxh8IXbD$bP5LjYtZ0z`=aSm1@$g}5q`kUqzhkZzd(;T3bvtBe=s@d4$=Qr`( zD-A_R)(Z*55Z{c`1HtF7NFZ-oy$#HzB;?EEb^A>lK@|lQSW{ngJ0f$#{55MC(HuQf zRm?flICQv&B~t|jCK7A(MaSg?hB94>Faoww{rTyw#OX3UX=I_AGfiS*%)nO<>(5WK zl2~$^y&fONHuu~Pm&{CjQbiMOPS&MSF1|H2;SodfqWAjhah|>t?b=J#E`C(-H;Ida`x!Jv5*%^d*po8oYxHPEsoa~~Jtn84yj0_;) zspCSBvs6HYM*9$kB4n_Y9+mq!W-gGY#`*L{mWc%?Q2wR8C_R4jKf zoXB5NT6*9i4@&_I@r7&30EXdP5t|4tNy&oBXw*j2(2!7!we|8IP~CzhzVqg-Fp*<8 zEVyv>_V%`g9_YUlY8P7C+2M@T)7GB1&Y}_!5bzCYu+uXE1UXK40CF_)BY&WjiV6&I zY{nzsOV@R9@AoPgGO8Q5eDBuR*AlfPK1kJ8S7h)HK7cq-@8RKg!0dZ0y7zgV-Za?Q zdJYAk(~kn`6nCm|sh2rh*{3YmSH)o!l5r(>fKT-0Z5wie zGR{jZ-}aEXikr0|9fvgLUAnpnmJG(Z7QIQ$n2ccjZNG3)a9FfBBG)XbndDxfKh#xc zdd!bdD2y9B?(0lWsa?7E1g54>d#24JLi#M@*S8ZQ2>Qhk`A~KD!?9|-{bFu(mXLyj z!(@+~`fLdD=p;^sWml!Jc!b|gYPg#8@K07efzrs>@jVTe88(cgyZZVWG97%IlaxL_ z_p7(W#w=ZLQWQcK5=gn$>4ePZrz^z70$?*^p?1eN!Y_;VXO{u@<+I(#7LeP5?xUcr z-=qYIj$R3Hu>z0^>I4N;*lUVUOh`g0DcUM36M8CfeGg*v0|wmTde;^N-)(WAZc^$#&E*+|sH zSNfH2Hr{>1uC`2f`x)6XR`GW^++vvrY$kcqV`{2ZbS$WAD#!ljaM{RI3l?ZCsWTHh zG~3jIh)8ep)F@KomChkfymW;n?$S}hM%B3XKSdGhOqq`xTKGj)GJo5P{cs0w2p1c` z4UvaiJ|K}<@tNIAfL0?Dk5hKIC*ea8@wrOb3;~?<<4%i+OXc;21P1na49l+I&D_62 zf>u|*fVnYLdZy0pG5hZ5I&@b}TWPV$)A4-uK}XF1wv5Y>9d;*|JI4?dmJht}eg9`@m~weaDlz`-LY9KD+@i_De z5FIu3TgY9C6B{hU5?j;QX9_;WE}(+i={8^f{^c(oiM%aqP#>x>xoK}b(*?x*ZSz|` zym3<_3q?6@GnEZ=qvR?C888I>ONYVvv8KdK@5Hqh3l-xhg5&_yQIXLlI=lHYpwh!W zCl0HUG+30_pvw48>Y|;zN%MNVhPBFL#dpffi)m=y1_gbH4&_!I2=aOC!$X$!1`){h*YS^6Z%-tbf0AtEz%(9kNcK_js4MJR6(Qus4>}-wqLDbw ze9F|!>==BBflx4admKzSvAWl{v9HfztDg3zaY7|#6Rst+n?89j=|fm}sh&d`RNq0F zHMIIQ5rkl&)#;#JbZ?Z~|9I7Qp{~Kc_oVzNC<1y1a`L_!+*e41>*Y0(8 ztC)6#OS0COe{?@+hzm)^!V(?9Z=RO?T`Dq!Vq~rJ`NCC;ci}{ux<$)3(f8SsQkiEN z3uNbApAX@(yZmtZTiR_^s`5qPTJ_JTsaRuLJx!Xv9yLZxP-(hdq$JyhelxF%QjfcS z28ogvLar$dh;h+Cy*heBtZc&>zAnSQMLMVj0};`!c<^BPs-B@LXaK;4|LC_9Y+Mg% zxZ8RDABK%Wgv1c)l7vZww~Hi1B2&!di))a5bKcfLt>{aet-3=kGRhkn8K^ma=p5Ev zTYEdHqSBZuz25A?r@Ii){I)Te26|XAvCr?3!s7Z90MqLt_3VD^Y-Mw5630P5!%5cm z;1o-60sjw^awMFmBTbL9tjp%{N&V(tDVdsAZPAtSL3fmc zVHsD@V3JzP(WCKWn1#%U;xZ`iZhf|3Frl{{6d3Q;;c>ULI1!Im)?_Hbw$Wg7phs#B zxwL9wOtF5Do0y%oG~uaf{C*|cEGPSOpii^LRA=`w?{+u|`b~>;C8B3c{9Z-n#fujq zp{?D`VFDgE);>OSES=zCe9XvAbK(LYz-z1Gx2EI2kN>Z^y8ovBmNAggcMjfilF-rV zC@PLFDEu4n$>x6=)lvUvqx%2#Z2S>XP*J(-?BqVUANA*#yK+RwM~`@*{N*s6xqfCg ze)`;`ZM=2b(kBHU>5z=GgEgGyi;WTgmhgV94Zq2|a)wNAIIp+N$cHZ@IYox7)R5CR zwI}8k_=PW>_iaIuFu{*xm42jsw{C*G95Ve!)Yb3LefTc9_Hjn^;!bU5s(odwXp)UB zjBHK%uQl;S2w3`w@7eHY51S13cZMu&?&_ERc%cST5JbH@EcRl&JZs47Fta@ zxM|>=tiR?4+${3($ffcZadaPzCpn2D9VW!YnM^g-PQt=DRr$O^TzzK*FVpijS#w5q zZoRs!rDge7vlixtDV}ZU_KVf3O@`6!v%W%V+n!Hp;+M8H8i8b{8^re(3Jz8v1xAyR z5!>E&m9kRWw>~+A+q21Yma@_b>F^nEW1=Ws6cq<#>qml6W@B~SON&4b?ZKsI2|N@hh`CtX-j0}26E@(k@HnffnDfckf_~jmUP{ghfl*5qNf`}@$m;v zC)IHM^uVi~{9ZO{{dgERrieNo_(&0mo0I4Du-99_vbnLo3B|UzJh2K#@tn6NfrDpa z3>48Nbij7XdoiOiFi1{+w|Ut5pgYp$3cC3^bYB#!S3UHJVs*UUP+`?H6PX(1=G@S! z?*F4)5M%}@O|{()wRg0`&%X9M6K`IpXW__-Q3=wwM^F8dPcE99<}YIgJ-KJ+Y7g77 zU;-iOTpnR2w?K6qE191T`%4%G`K2A5B~2|knc{PMW(|I%LYGQ$@qJcm_>cpbhbG@a zDINTM2uCo!MNgmMNlItum0DSh(xRr8OpDi}f|)4npNW`i53kD;md-`^>7aP_&}lUt z_4@O?X84}fu8TK&NJ~xq8QC-8?eNjz%<8SW&qga@&^w?@cDye%HDNqAS9l}MQ|g!|`u6-i0J5gAhF#Zi9cAEPv}FnXurV$+OwLMn6KZP2qRzI~AIvw-3OaR*X$LBj z4#1FY#LMJj0opdSRRyl>av2_=AxGf699lz6FC{JcdvkNC+Q4QDuC?=OD-Y=q2N&DE zF7_$ZpNfIP?e6HQHDj4x%e0;f%o1Ct4;j_W?a`-Z1p@}u6%spL7g6i(RFgxpGQ*9D z;EJz@ZMw~#HrlP^KbLrz_-BWAx7)8z^OdO^&4DX8d(Y>|@0`J}$aA@eZ^YO(-v@d# zZ2P;oB0M3H=^P>ks-qV^zScHus*0QP## zgtsiuhb}`QkWN16ZU(;#-c=cLKHnXOhXF~1 zbK*VEGbC^RuD4vQPIs#=QcHsrMKNQ^;hSs$P7Fl z?~f)ac@g-sAlpbXw&3k;pPM4A5I}wFDW3gD77jKCZB=;@6H^n9&HBARE5vwQxd#(X zIovL2U4Mas)Yd0YXGEW)pT|hs*r536%zYo$4@2sI4380jFi~8X<-p%nTX(hF{3^1q zuOGCIBMTL&(Kh-Vwv_3;_wn&DHJ$vrnzu2KF1h$QHg>JHB&Cwyw=(4-q3uN#&n2NQ zrx&X9ci?qAuCMBKTJN~&Pr5PG><9(CZO_6}pm_KX_yV?J`T0R@R~1#ca#q&X#xpI^ z(T1|Rx+%DdYHCx`@1J-LOsH0KW$Og~l|g?vgPig#+_7w{+@@qPWh2!w^(r;lee6by2b3KgVt zwst&SQw&e$wm|~|zwZOQ8YX$Gk6XvSs}10OpmzK%4`$l*#X2>g|A_#gn-?vx{D+?P z0OWv#`8xD>XhLbFwbNC3CI}ri*4D+Pr5-l}F~-u^p5ps_YqRtI$7Q;DbGHR9Mt_T9)X}kVfeL8H_>eG>~8mkr*8d{O6k$%S=e-&xM2pBr0V!Gho`7yI}_Q`ng z)jb$mM(7Y1Rc>E`(!r_S_wlrim7Uf759Y7%*oD_o6JQnYH3lSzlj5F zaki%FR*=O+c7LX~0=1E`vA5s7;MW6Qt_pRK932IPf*p&;seIRpFH@0ntHW4@*Kck- z<85pmrYk%;8lGgB$)$VNY1^Bqh1XxY5;_8FPfmWk!_ZVx>KGm#wy6f8?Ll9AbEH+D zeV%`=)_S(i1UKEKx4#!9VJMw#^%t63fYmuL8qfl>?aX}dAH^pGU2h=YYAB-CoSgHmKxaG^G$>?-o_@zpzUntJP7#& z$r~(*8~9BU5)u`#>PZ^K=w^$FiGkOwfw{9Yi_MQ)1NEn!?IkJfpc3_)i`C%>MF@n? zP}n(%me{A8{?{cJDY3?f?F<6RI}0i2zK<6rwvU}Yi96MubFk}4g3WI_x3=|#ToJIzq`%>XBt0VYXGTPF5U}^@?Bg#48QK821{;5<9NYQKh}D! zZ{hwSmtSvx|MlJDtkR9b9FF|^?dw`U_3la$=RQo}%s=Uf{MWoSYDFiYNyQwK)wP!|Mi+PH;!RNB&dq-;n_iR&Blay4*+~dN$sWK9Q09yvDLvPkKxOT4nfkFaW zV+jFE@-aIM==TM0$I41DZW^D9_kkXZTG`V0^mymKYB{cA=lY1xwIZvcoVbv7YBr+B zz?wwu&-Kr{m0h9MdFsa}Ct$C~hpNZp=Np~*Lv`ATJZ6*gGb~-5B7m^ox5*nwVY!{} zl3u&Nfq9{soDz=DGTX!OQJr7aPx-EBDph32IwVe1P;j~NvH*TPShBZEv`eX&wmk22 zoH!B$JgEqq>r~A)@Xj1F}CHhS@dM)O`MOAfm^$#DM0TkPaA+NMH z9_(9h_L7z_81FSjtC*2WWRH9iEetjrUF-{<`~^sq0F7Q5?K#duoo;KRHd|mbK?@wv z-fpn~A_%~FhegHSYd>@iP4+I-r|jlY(J-1FCR9Esvp%SNUM1K(`t~F~+$bx{NM}fo zw|c1{QaJ-uM_xtFwWtG@*Y@~Ch38=bm7qFEDyO^Io9#B^%QSR3GK1gB#%5u&S{ek# zZ{5%N{jnkgk4Q7P2G1*8kL4@9mR&GpN9SMYi)h+&NOUn z=ql!&Klgs>5a)tVZ&T*M)$^Mq-Co}{v*TI6Sh$7Fl+nU>wO!h1vJzKPaAK`F<9AzH3Wx2&YqI+Sp%IKT@yI5J zE|=~-kIu2~m=7|lv|7wIlk=s!33n7_ej7-JNI0xV?MX2)v^KM)fA+)Q&Hf2se3QLn z^o?$|NRdG+cb#-w=ohu@hl_crXj=e7muOY(935WVuI;m5mL=`k+P(U7ov!DB_Z9)o zgNu;8#8!ljJ?MH{9q1uDoMZjcse%1Q)}}Y+;E3rt@UeB>1KB%E-57?5fKri z`N;dtg$R%Vm#T55S^R1Lx$FCLyY#*BnNu6JXK6j_^W#e>J!nvK^ zBX*XNUa4MHq25vp=u8pq()*reWIB+prr7r@^-n4EH&)NN_twF)(|=6D4~AzO|HWqf zzz2zc2Jp;!*(l&d0DFQ7_y{${tso`gHIaHhil+AK4fM=vtnI9v&I52a!+8!}tu1X! z3rZz#kBB*0n=1J0&ddJO3s6ykAMT6~mT>+Dt}gV?9Xh$Pt14cWl-AdCQL^sI7^13< zo;+sM($mrS-IE`i+|fXyA|_#d?iH}K%Qz2RJ2&2-S;eaN?|yM7 z3W-WD9eR7O0H3h?kFQ80@FCEYQ=?!x{a#u-Ic(=~dQ)??>bll)i-_0axtg1uz4pSW z!<9oQEYk^}*Lz?}V5DypkAcg1uXAwDja(-6V*)23XV|@0MshB@Igu)-aXHSZ+`%#% z3o{GoE<_dr+tdC0R1{E_7n|Q93l$dLGc#8;m}~ugMv3S1c$&Jh@sSxzkJnBf9$ca@ zFp!arbE^u!MtpqsQayiU$Ho016>@$?cIN5raup%%y~7#5673e_>u6Enhu7#rgM$at z_?-H?U98kfesH6rVRa~}7as!*yJc=g3;_UNyTutr@-}g2C;7)F!+0S4GNVxRTL4`C z6c|j#ZZP`eDS!5}R9AU#lkn-_Lq>kLS@tK;uh_c_m~Bb~1*1q`U0qvkJ8vW(Dk`bm zo`c;2o9pXfX+D<`Oo81wd1eOuU~hL}aq++)!`qQo0U?PRyVfxHf4yZ#r)MDi zf&wlRQ-z7iI;dxHb8EtU`9d*NM+5;As!cL(QlvbS*rLaCnk{arUf_{A_D^{C>SEY|M)9nZegUs%q@h$kBha}pBtJSby@hq<-PY6!&Y+FtnsFZ+Dk%2WUyyl-g)qp%*T&npV{o+|UD6nX-c}%Onef#mk==FWB2dj);E1o2HXqy&&9!xD!&A0 z!oR=X&3GQ%{}=UaPWXT9H~nu8`@h>)?_^t*5&^@!K>Xy0=H&QT5LQD=3ydhxo4}y= z=Wmw8HZ^&>1%jdYVEz|pZyi=;_iYWMh*$_nDJh$llI~D8-7T$jZMsuLT0pwHLAqNB z>F(~_bZwe<@%*0iopY}5pZ9vX{Kw7aj&-kluDQk>W6Z>VDZO8LlA?tMxDO4-12~e# z{$=zw({ZBzKQm$f`^uIs9iMesKJ2sDYVG5mbr{VmS+GY=CAiTRpykwbq`T#$Ak8Kq zyMHY_P?|?9-5rY-XuThJzCNcdg3mS~23toQ)AJ6y4T9=;?(0RKbVatt4n~osEQ8I{ z7lweuXuB~__FYcO{2F`*%%voBLejhjx2=}Es)LPtc5l%iH)AA0Rgg99hU1t`(GwN% zoJ|L7Bx2jN_y3jn^r_Q4{3%rw*y-7()t<6mllUdYYsk8uPbLLXV@{VCh_)n<7pZ{j zJAiN2+H!G*B}H7d%^ka&#+UpA`qrO57vy@ahjT>ONX>rN-&)R9ffg7fZGq2@1PT|T zdT^J@Isb;d!Pz{nAPT077jPW^k(Y#zmod_T9!@t`GdA=&@m8sxMhpMGnD5|yJZRqN zal<|%z2oCsGy z?cm#A$e~x~L@(|wJBZP>;DfER*8h@CS;)!W+BW90nCb25(NI_MSUunR@#9w<%T55B zkyPc)&*Tdlo*C1!sb!yIjE}79r(6@XPoIi|E!6RPFKYtpgMxshT|i$_X16z+=?e7bBaP+hgwa^N2byT$zr7PyF%^2o4SM492K)J6)eB zrcl)ykU(q+X>!o4&4s`caBo==V%6lky)Y1A1v)_@Pc1y{7~hrl=>VF6=J-*Zu8|w zYHDx53emva)8bHXyDyKHM2tlciV+wPOr`y{^Lk(|;*=MQ&Xb})i|VtZTbWoBJTy=Nyc;xWw($<*H$$CAeQ_$H17^W$ML7T{_`g-0fuvzG#c z3HlK;zglZZl!rS&-kRKFrc1rREEO99QtF2GEPoK>*nfIg>pDp z+3|mjad0G@^Y429OkFgId47T7qYxer3^XXJ_VF-`jo-^iNmT&F{Bl--nF>Qh%$2n+ zBMsNua1=owecz13&U~Ho4M_D4LYM_S2n8Hol;#z1SnN7Zi_@h|jMoVqPnW^~PpkU- zqr$+yOPpVmv-1OS@o|B}hX252CnI&{P(Il9!{OUN2@IGb0{CjURS8MKV?m39A+s^w z6c4<#pO69+;=7!gBsfl5lb69HuM+WhId?6-kGVkF^dv2M9d{=Q=_*BH&7|Bq9l4)J zt9W)2z#yfqu6`z_RwX_GQiT~X3-ags~PifXu*;5!tD-jzBoe394GBK(S8fAWq9 zyFHKy=Mgpz_OB=Su9l^U+)^`ErZWqFiZfz?`t-@4*{RgHk(BA_C-Yx`UBaf}oR&)b z!%J5Z;*>ZZ$Gt=zo^oPe9+rfvy=#n=&!(oYFyG8Aw8DnJm-_e!n~lQ=ISlp7vsIwv z7*G7EnVDTSe|i?rqy-Z)(7NEpk>0qOc?-P2{daSU-3m9+-&3K z6`lI7vIXBB)Vufn7EJLE507xIN3UCVw{0u=L$AT!>^@<+dO!+b_~lER=RJiA$vyJ; zDk{Oq-oz&et9p`axia2W>)d(`#$q^!8eOzKJQ}L6H?lr|gL*SJca6MX0E!EDV?Cu8q`0=oDlk2Uz3S|cr z*cGaa+QE`Sz@FP|GZz1&w|9o$(b=sD5v<`CLWrn^cf6>Qk&@u|*j}KYaS#@6&R5KF z_^2CR7oV@OpA22Kofyd0BB1o5gs>+h{rs7mm$fzB7w7-%c@TLq6PCOaRR1_z#hg9C zACi%tz-mEGpa9iZ>RIpe7qbxp!{<`x0`x=Bio3-yoPu%l$uhoo+%E0;Y|B)q{I0hj z$svdI0RWDwY+f4aPBpA`n(Fz}#1|om6;>M;=k7Wa6w~N$3@UHtxCINpmrV2X?+39U zddJBaOhGCA@=p=jee%i|JBY5o!T9|;4a?nZ`*9+7p%x8mv0o zy-{YHiz(O0-o7D;g3t^qsU%)^i+c8PnIxP!_){dLg;!t<5e$3o>gsZw!yXSVmGDzsR-zDmj`XQ0*hCGp!Sbuc&y<0QqOg+-f9=YQp`4y!ViO2@nP zCIC(2RQE86g@eP(ln#*3>c+pa6tj7MvKp`wV4Sm&9MzvNWS>~E`bWnVh#m2Za z&3#f2i2yNjm-Ee1Z}+R=oo8Pye}UHw#Q)USMM9ETRD->z>%#)i)Di0vEh762gZ5!IJDUgGXMMvs z1-(+zK9I1GsNu7ynEB?%@9il0=#cjZNZk@ zs@>gCPaH{DY$ow+H;2gl0{x`iP*M zUJaB@nIF2V+LV5xYsVqyaVHL>spkLcDkITs{xRqLTK3`P zKRZ4-_J6mX-QaLYztR8v9&Y_ToKeSyf+Q!jbaB5v$cu}c8qSVc5{kDoxq}oB87cA+ z_^#loGT(9k_8T0e-__s$=lN1abRSdT;aacLO&d&^e#&4ci@9k^8NlwDZ+TkF%kN5O#1BE_xxdlT&jeLTgdAmv zh_5$9JL)@pc5A;&&7>}Z4mehFim3n{NXtxBX1ZdfVs@;0hr8TkOdos%Wwn^TjKQ=F z*c5WhH*3BCS2Nr6#jtlILSv%uqz-dun~9N>sgN+tAJs$uua<#_zw!DvBG29W_chJ7 zpAME{si>kE32nDM`J>83!P9&C&*~a;K3%vftSQb8D>R=`e}litaBJ`gGl|>Pk7l8C zBT?|JW~s5%im!b5o`2po+J<4N^Cza*5PDn&waK>HTHsa&x#d3?-Vb{%lAjpd0}y8XPJ@!Q^v33-sT+6d+BX zpWAI8yR>usbUnkr@HJ=69}19Q>4)AemoL<3N7{=#7i7SNz$b>~M`giGemnuN4Ab{y zLVSiFB^8tWkB*+jO+}7ML0(a1UshJE6)_aaNy;YpD=3uvQs|wnq&0qVP;}%CtEdBju|7=c!5n#deRh^b3D#9;%c$99glj z{LiN8vQXd4I%s5a7=6)SDV8;ZU?>BbP$RvkOsJb%FJe060kPK6^T6n$d7aBKFbtZ` zHpXc5@+<3iH*VHd!`?su*KR(|N}7%u3W=Rc^8~+q*RGWBVM~C7gl)nT@i=V zEvW+U6?Yi~^C6O8+zqq)f%1N8rA7#crKvdd4=JWi0Scd6m(r)|wMPkk@jNgLO!_#E zqpJOtN)4IO%J*BJCyxGP9N25fz9tw`XSP(j_+~O!_Qgu<9JVkB9?hgVJ0{wjYL~y6 zw`Wy-C=ZvGuqFa>gEH(d@KKR+U){%!)XQ*p3{jc|Oyjxyp|obayVAp4#eJ?2BK6%V zcFQ|?#wpY0-q*}}w^fTj{kbcHU*0rM)+v)g@>M5BH@d6PW@P)cPE63w!Ph=X}#YJ0tuw#kZEk=>yU*e6DITI4m zc6*a`wjJXXmFP%vH%rMM?}OmiGMWMV&F_fS3%}fx^7~Ja79x%`{S5bLum~@;>0TWjC+0Hzy zH+RWEt5k$KPjuO)YcHsao~|+Gq9x(YH*H^Y!)V6z=9Sv~lw=XyQSEZKzUblA&Si$m zNh)$Y+{g$v4jiJc>(e1nMJn3Y3@ED6uMF|EGaY!k{X zkE^OOD=Tv+p6~KshJU+gBK3Rxb-25$ORL7tfNCST@$zMZ+gki;`Vz=|6HX7AUfSZ( zz2S0zh+ikM((<^j&*~6y*UQ8)aq#fu+@)f|IV>pBcXkK~P;&qq9CN69%=3(Z^BZ;kmSO_&4DmXI|ow#SpFfoc`uBY(X%89AtnB4C|N@vJX zt~K2r4&em!YaNB>#*wZiu!Rf6cX1ZNB-fqEN)5si^R(5X&s7XF?s;d0Frxd!THEkr zgDwr?6=o8@ZAf1BqOCke{?2XK9I`?J^~DQ?6Ew21CyESX?uU@PmWJzZCgtl>j@#^4 zF+Nga6z#t^4RWuG#9oJAS^sM3N1vUtR}SLO3SIWX5dIjC2vnqczwleYQs-f)8i-Ak zRyVvXLpPhL*!RYBxvj7;cr`m%O=>g7POcdriv zZR+z@BdS-~awbMT(l0c4eGzVmzlJm&tq%5Tc`v6h_CNfM_%bq{1dKys`cv@vY&P@R z;0sMbSd940#)6$09nKdQC@4M%Z{i>L{3qo&)jNxaPSwRafx+&sCv98AMRUXg&Js|- zURXT7nCGy>8Uov(IjuyB32qOk1@GTHSBRau-4)lFj!>Ex4J0R5-C51_I&KpjzZn;a zF=10xz;pSc+sCs{N_x%r=EQk&*{AusO1JAU;_kM|VP%1*>blhQ0!9K1(S8qG3Rg|9 z@79BjLQ J55PJwtcPD@1@)Is)>w@#fJ}H7H?89TOY^3vCh*Pov-$MC!D*&n{VgA z=^aBisstAZgdX2ODM->4NOEUemG_LO=8b6tEZRjgX1#ZpG6wqy#(R_4ZU%?2zA$RP zqmG2!PmZ_`YPLqwSfTxwuN}6|USaN@%+56HEF?@YP@ijJe}7l+sgL+NN-4qZjqTam z8LVi5FnErl#N#~P zeTVor{oIOhw_g})Z{23Z6a3d3m%pz+gK(P;6c%vPq;uxWi9vg`@j^LN-6zBo^5`@~ zSM!CSaDu!5O?&bjifUIX#e-xre1mM}X-$fvPpf213>OxZcT#4-r@><&UXZLl!N&F| zWkk%Ftvig>>SZ`HM1izMN8!Cqy*PAYm`ew*9f?pZgnxp^=G+z1R-64$Lep zyHj&M{`_rJ7(?DaMfvLMW8>pbc1I*FCJQyHKv8&ms*wPbYjO(MP1f1jLekH_x82VN z_09~rsa=UvCC&MIBXr=MDsU&LbBBb67Cg_V)zg6Rbb5KV3ry$8B`s!;tl?v=`QmyM zS)ra1j9VB{mx61=MK%IFha!>}*0lWiYsBVS+>XGgac5&m1iFMN|Lj>-r-P2gRhdL> zjDN0mhOeXhjZg|NZ5~Z~TK68w)RQcB`QbO2=LFfT*3p>>jY%E^pLBfQN00Ql zkWwoixji^eVLN6FYR6?92CSUTnU0Q$yLI{$Qg06h`HP*FA7a$_0uK70jO9KV59;u+ zbC{VLe39Rn>>45AZ(~$rl@(TA`@N)Rn4L`@&k><%)^#-YG3{Wr6d6kXDu5p*mgKLb ztQ;Ulje&ug6PxktmlU*DC&d7G;9+87g#*(qr+Br-NR6<RsJO9q^G!xhb@v8B zGXYa4^&n_2W~S+v!X5==8eTg$Uvx8|Xz0sN&%~$vPcRRbGsU7v`ViRsMFPu6T;Eko z;f1bCH|x0OS|~@Nq)EdT?$N;7$F#(o;$xFpRvmKP>{!pZWrAH+trwB?KDdt!pG)Lt ze4>=K;|eATr`3P#3dMcKm~~XnPZZECApun)$G{Aul$2k>yJCLVAolhZU&RC@JsiE_ zRefr^3Iww5i2f&(JsKIwLyCxJdZB40AvxK4X&7kRa9nvby0U~Gqt)PHU!Ql4q~fLixg(b#>%ct^VrI zWNimZJW=r+?IypS?SmPS=H#my9hqKD>#)Low++k%=ihbD`Qt86XWOyD1;TxrVaFug zyMhdrn2bry>z6goR6gn<6qOMJrW?GU=XNoYJ@^pBtyMd`mmddOr1NF=F=reIp9V>= zV&Zid(o~a$??zT=)w+kl3Nz8OY;!`u^5Or(g`yu1ha^2*A6*qCMQiKduuQ)=f3vfb z$YR7;^6A%E)$V;9yDHlSzFHpsvKY?<>PJ;C8#4*Fd$Z26@NWj6`FQ*NIpNp>zv0bFN?fpn|G~W zwD}pLDjlGC5)~j8Bi*xI@HC+1`FD!3u*`n&;tbSTGskt{$xGnD3`;~csbIn?qHhivBP$0*+L+1&Z@Lqx_*30KZp8) z$M&oR5Mb!GN24{TGp$<MX zg`qdIS5};wQ-m%jonJUC=4rOjr^4k+)qZ&(`xQjNmo4s}IBY5%rPo1?4i~d@(mZ@Z zu@oH595(GxQeB`pqe)o1bE{eL*}_F6Tci08KxDJ_eRdqye5uCE_rKnHi*Qa>?vJ%b z`BTcgwq1!1jgU-XJX5!jZb$X~w%ffgnWv#q<>ZxA*q51C&h5Bse^{m(Hy*{OiR0#= zv>mE(I7E&3Y&QREYcgO&=5Bwsto`}6`l!6zdHbH{*Z?g(Ut@&rz^vvL6&CFiWrw>? z5o&ue>T_&tx7J8I&8}S!anbs3B?fFm`n7M~4TL!tCB_;lo)%r2$55d>zIFC{<*Xp- z$fj(W-BtFidx2=+s=87H8I1x`))2D*;apHo;Twh4W^#u|kE-%Sw6GlA?PGC8cG|Li zPH=M>*5J>fnof7K2)NDBpRnX4dW-rnBioUB%r z3m(8rY_tk!UE*{oxic)4&ujHoxC}#V^v&?|RKtF4QzPYv?ZeEU0X&CEzpcj6+8S_^ zr4v}EZYET2)GM)-_6FN4!J3vZUW_U5F-_eERp+4`kvU= zZ}n%}OdLYAU_6|N!`E%t>NDI`mGIu{_vL0B=9tC0F!(10Hqi~@{vwMFMr};hCabOd z%EFcAJ08Y*Zb7z4#T~{N43%dS#p!pI4Y5wX8j-*y+ z)zp|&SaoK!&*pTtne3zvNXAv6mZ}>JWucByzeJ!PZmV@Vn%)RlU#32|i46xzHT&&N ztJ~$MB_UMzBpD>7>`F_nA(R$sQP^`f3W#Y=t7%S#%Gd`02`b^$yp)EXOf0zGox|cJ zIjHg;sRxRYtJj(jSr$yJudaHO_O-M?kdfUDxtX_>zkFE&b?! zK;13gu&Z(iQFBAG>~j_;8=ZR#F}dKkev}XwJVY8SVI11_qvI!`0V$W5x4&jRm!r*2 ziQyb&9&Tnn7dIl7#c~M9C}O9;z25Lz*tz$(M4xUaH#=PvAW&0V__2fVyImRK4XTNQ znjfH|yS4VpRKnPzIM3pa;9J+o^sHxWs?D)~o8$jdr5h9nbtDut!7~e;g#R)Y2i8J{ zuKKx`E?NrrnLYyYNj}f&AQWo9JsRSE9O9qMS4YM6&YLETP7}}u{5x|CCK_;*>Q~%r z9bn~)sOSN?B_#*zg*tdu_7A_)thDZViLy@ssymuxR!tkpp08d-6fM+@ffU~l zvCrS&>DZUQCM+xrn92DIhIdyjW|Os)yzW}aL?Fl}d9?#cv`9;usCKos;aF`q)A zP>yMny4Tc>&*@+E;!=MKBPCZi&Uaks$tIhhzY9KfisTxVtuQK9t*OLny3VqDSDm@K zijK4xw)WfW2w)bGGpy?X?>+Qx%lJ(i7({e65MN3rtQ zUarQCP|+4uec9btKm$^NfEbBF6uCmy@y~n}BkGtv)~@QYjBio-&Kzj%A=6_@?h^W? z%5qJ$`0#~se6-czIKxl#J+kY8nx|7j(vi(I%R16yR8Z_n=TCByY9<9ymroSk*d8DW zR%Va>s+3mlkb4K5`XBIfZyx5K7Dr9%Vly0d5VA84wG> zvAXkkxhlfEJdU%2fxqY8<3~&srm(uanN!mA^q=tA_X632RWLK+P`o!c+wFu+L+OX2X_?wU-|5 z{9}#xte@+WfRR+h29uGLa%15%{e8;qh-anoP&V_oNJd7+{o>}^>1s$bFIWHE`;JQq9Ct1R|13Q}m2Qn@FvH5Sqn#0d9<$3l)$XtjxezBv`^(c_Qq-i|B=a(q(xPo-p0BfkQH zT{c!Z3M`NdDu*$i`Dil4^sGVr9-p6`yLohG41&lB;w1$sX{o}rW`lXuw;2L##==|e zh8Lmk$5n3d#{y4i=;*@V%{IaJ5hTmBgm_3wA>t#3vhnO1Wo^^rhd_NCj&;D7_h(1Ow?#ez|8bPGopI}$~Q#6e<^dr`*hiA=`4rS~ZhE`0CoaA?4qw;+tUr;YhvIQc_|C-6#Mz9*u}Qa$w`RPfKxWt>n;_ zPKMgo_)WlL16j)tLjKGG#SMU|RF_(`N8({)A2{(^v!%(!Mn%y#*?aw2IpiV?apI<> zO^6He|6w|m9w+#=v%4QR0IHy&AN|bdW38lQO9iDGgoB(vg|SFQ?i00ryK25-I0(Yp z9!*MV`l#(M3Ax8}9D!I&DbJ>sFZ`Vw&T*Z5>m?u(!~yp_P@OE+TVTz-(;-DtD(q1$ z0tJ1R_k+!LD6-*2QZdDv^=@nGCeNB1kAC>6$jh%AX8Bph>&@nc#=$=uph@RFBYa6U zc$I$_;2GN;g^|t6OAJ~(rT7Ci0uC{;4D{mS0u&k_Ja_;YC9Ln?b6PKd_wxFHQeDPC z%|Mso->!=}bf;|uZo~{Z*dU3HmRLZrFDfdo{nG7)vW-MHfKY`YLw@LoSf0c)^Vvx{ zzZ-cRZvY1xm`Woy_Gv-6)xl+NXn5EK!BCXp?e6!|K{Pa}n~{3)$VrQk=VW_|kC$0O zQAn>tr+x+N6&l(L6*?{IDGw+8$dHi!XjuKx^Jkz(#P~1mH)qV(9&!iEIvbesB8Ka} z(lg*g(k8ja5!+DTIu~sZ4>z}uyVL8>HwGvmwWN8}w0O3hLqkk+TJ>OaNkK^=+#DFk z=AK_DtuZAdeumgQR0KmoMWre$>v&jvtfZtw&NDet7*kW0;+Yy3m*=hJs?#iJy(BZh z2a4Zsw%#lgZ*6aP(!U7xHyvttg9(mE!f6-Tt^k1LLd6_*#V1J0GSt}GXLYxV%}oF` zR4sCmO<@ibC%}Hq^2M|l{wYjsbgHz~x3U1lPyn)1{UE|QgYa@w8g8aOXzq>s0JV88 zo5HnGdqo1&ssLVd3))jo`reiq)m&_leT9JcNNG0N1k;!=%x9b4;i|4b;$XhCXT;m+ zR0R{{Z`K^5|H)~!6^WTKoUZ8LT>E#QP;z_i6m&G#%fPnaZ`e@OU3g1mB=$NtE2hu) z@9(jo*o1^`n}cbPeooxi0u9RD`ny*C*CKD}-T?zo;8(VxuK2e=+xFCtUEmmrn$N)hC0`U?QgocF;3HiOS+g(qL zNZ>KsD^}0|e#Cnld+gTNuOaHqCNm=&U+hg20GRe(GL)Ey&1Kc9D~uR3Y^c@uh27S6 zWJClHJSMcT3(cfuQl5x#hnUY}xz=cbq(J4Z|y{BsdQm{r$e9#6{CRX%*%ne zksRF{i46D{H*N!!}mewGWT9%Ux>A%4|do0jKFR1Fx9ul|y1)m+<5`su5g(Ko~u{+ruGMFB`uD9G&JTuQsYiN?Z`FftXf@$(F zjhd|B^Y~bdC~DLh8uJ&%mg?OTw>QLjS$R_vg%gbBIrl(?bEU#-K9BAnd00d|+)5H= zNLfn%Az}?A^4$}$qzT3!J(}M*1OU3l{mY#lolSZFek~l;CWI&A~u>jEHC#aXdl9C|2D%ubgl?MU>)8^)9OSNJK%(eA( zcFXylssXJj8K%AS^QoCiqjvzU--1s-6vB5>Gc!75z*D*)<@fRj!DD!Y1z4to+?jQB zEX#$w`e*CXUn+8TbGZfhB^qk|nLS(`Bdo5j4umT2$LHR!1uLzVj>c5z6Id3WqHSwv z*SMVqaoJ4<#ke9!Lj3(9>8q>y*(H)2A;3oLhIe+{V)7L+X8Y=3nNKhQTLT=vKixMq zp1rHOr#7a?Ai!dB0@}kdY92z;!ITJMlvGVSdQl-Z(EIF#M`lg|83T<(AX$3sz&+pC$;OhJwXoUly5W`JdDD=D2iwfYmZ@AtulaLO6GBV{!=z;o*{Tom(R6v%VRy;-hVDG{KJR&d=LWf}(Nm z`=ixXNF7G5wuO1V%Od((rl6=u#N*Ni7WrcBMvp`0bbt~pOFjhmh#E?$DzD<;u>C77 z?e;1jZQU>%&}3lK?$>pmm%Qeiivzc;*qCp|vQIn6Ik_*Dj}4`>Vmu_mF5Emu3sk}C zQ;?NMgYhINFpyERp-g|tE$?zXMLj4m#AF`O)>eJVi*VoI%#GKbb|`DNzaSj+9B4vT zNP1O$w`_2}=p_)a;(SLspD(_0^#{F;2;23Hmu|1s?{V}gc=F6V^b~BsrX6Y#`t2J@ zH*Qm4fXQgdgM>bhOFHw_64Kt`1OHs0`T6{wRnY!tL4Cvou3p92Ff%2 z0-U8D_F-##Qnv#R7oENl8mpu(IvXq2sfdIyKhe0=>KCd~aZE!b5@J1!V;V37Ok- zY@&6|m!UsQrD!~cQ(d=RT&=N-DZ2~xuIFd4H%}KzI{tpA%r}Y zi!oE>hEfI|@oH*Wnb}MU(Yx~EKJ*%_dg?uWJ!I)13`gYL`TZG08f#q`Wjr=%Gb6K!WhTl$r8ex&0sH_82lX3!clYrxhR$OSI#vebZS{96oc930HB z_;`HGuKBk*_!21#XrF(jF&73;jbzTc4&CvUQ&MJ>uu6j%KiyiHa85e@^3365+IwDz z%~Ci{rRf*|>XZD}89s$aM>}j!{`s}DgHv$pMtE5fY+oP^zs*&a9GD(2ZT8a;{QR@F zHqYE0RFP}Tv!xT?tC+T1W98VFST@@oegh@y7;WNuN7(M-=W3hNlmVUZI$WN0gglSF zh9x9aJFJYWSDuWHjY6U2MlxgL<2L&vSAbryKYO}jQ(MH)PqedZZ*S*uXX{;6lqg5`1sn@+pYBG9yEeQu=iVUGNVVHp5Y?u3!<- zjtcXsYlHv?7!eQ~+3XGkvu9^}D}dVM%@0y>8;vk|Fn?bts;J0gD5$H~l?B=qs=Kt2 zkMzZ3hAHLL+1X%M3mi~|Y9+gRe;(P_%8?Y*#N5&@shBv_WW)jx^hvRjS=p{EIGp4fxSm}@4Vli8B&jtXQ`|3QOK z;U{_A>W!vxqRmZJyAnJ<&Ioe9aM|7Fbcg|+0q7`+5eBHcpi>Iybo2&PcRD&cu);z@ zLgM1ofRSQCpNg{bnoX_2bl{HO&MVtBs%6<%wlGupI}3U$st(ZYinl;VGSnZ)IQ(AV zmpHVp1KZo^S8$I(?*&x`igalWhXbdEnF&U;*Ua0S0VALsOP0=IxiELY?RoPTHg&11 zKznAjyACH2{O?M%qg9D32(a}*EDw~vCMWNCHF|;`Wnj}3P<%T*Gc{dd77-IuoRiZt z>s=2!zD!KibiYwwULZy|S>SODmkA#?Ci9e)+n$`*#XWc$E_pXys%DzeI}F}gEh3^r zq{lD)ut-vB^oMw--U^t(t~$oCzw4WuSGXU$7*3aFAlrI-ndh{{bC}g#6~lV5xK3M6Wv2g${c;2-5>xAg_gv^;+PCnqN^H4bxS?y7>n-7y0yDl1*itVq*; zU;U}Abxx{3g``gu>%}P(?#!4O8`qrfk!t8jnYIJq=+5>)?0;5*2NGD0ck65R7_@pS zXp!{)VR=djuqlA1m%!4&DHM;eY70I9{j$T?H+~ z5)$D1`y?bJ0-(7pItjnN=dG{L@Swt%fbSm-IYqTesmN#-W;JDsW+w$N|4iDbX;i;( z!GOF9h=nWvLI3FAIi6hzvMSL9e!$=7CARELO#x|-zZf}oFbR*%?W|f#dq$o()CL~Z zRb>4Cl}JhEn|aQ^bb7UR0O~K3@2e){VqNW&+lAVuMpv%M>f-HB-DCpIf@c;kwLSo{ zfsK_As_(6^H8eK1j{tdw0p+8Ie`;+vx*OBReeoH~Ii((IDI_&XOG}K5p3q)Gcy_Fv!+rZ_`Gu)*6>2Hut} zmz}m|Acp`E4^;GdZO^WMelYM5V;5u_ZBSD@eBu{xEa-_)zfP=*^VyTxpc)M*TtdRYyx^+TAVMyP*p#TfX<&u*Nm7LPiEDc}0|6s+p#?nk{@ zF&*F9d-!Ir=yZ+_QOu?BIm`haJkOR&kBlr^e`^`#bX)xeG5=riy;FkftoZOkM#_3! z#|aBfhKadCJj_$u{njNbEvDD^^2StVYBn>N;uuq8;6F7Ow3mJ(P3~7fAsKC=MMaeZ zCUo?vTE4U9pI%x<9iwjNa}hn{zUHgS7T0HK@dy9ZY7i3jKevFiJD~6Z(kM2C`_+<0 z>EjDS5*0Miuotv{0u2Shz69gnNIjv-s>cKMtU7Uro_ z)p_DH#e(;O(%+7GzH5Uoz;Y-QfFN>HZdFfUd1GrrtcQ9q_qhlLzJk0E_%=&qeNg$~ zQ`T$F11d=*B=K{VM`~<4|MKD9q3u3(`!6nl-+!8#{NECN!4>>B-kCshLPVREmZnVf z@Ol$;1TElx1b$DQL&EAOa`_LCw&wdk3BLEEY7UwsWb7X>N!a5ze%UH`6N@1~pI~L7 zqHbvX`F6&?`w5T%fUix{c)l#mw^Xt`U{ysF$>6$p@}FbG^pHZ5eCY!^2dm8H6Ar+> z%cgvomR{lhFTQT4C302x)*7sc(?+pXhKOcXz8}>bO5008=f^&H)OP5 z#SKxB?<=PFVo@YF+3pA-^jL0HWl8T%y_c}Oob0sg!}+!o$Zf6aR%pcW^NWw!ne&S# z#fUpKn$mv;LJ8HtgTc?i8#n**@2+%ZoEp{hxhSG-(>x$BZ1iV@$ZIl)CISVDOl$Ns zn!1uo&Hagxl#6<~D!d96)ky~wRAHa7TAAp7Z`t1&3%`eLG*g zP{HKzKc}i>4qY33UrZ7vF9!84d=VF3quC$7h~8Vd!S;tFyOK=#_YxSAfBiN%ypd3#yb_8!z;6qQRxVB?#( zMLQFi<%=nQPR5ajs=uR?Y}_aB!47~{>yF&4*tR{j!_+Yj_-8sUb||hB#wSGKVx-Wj z9Wms5$`3iOCG>Gii||RN=Ff(Zs-XTOGAJ($DM<=@>@JZS(5WEX$Xb9Z@Z=4%ZXyiT z2$AAD->{ZZa|Gb0`^|)wJ`C=3a8Szp_aCq}>Q^t99iUns8mxErg5mT4ccG{LT5>#~ zXN~TuQ#Hc*h%kj?-4ML>aJ54D|J%JbDP(HfS-;&*>%~#P6mvdOpWkM0~Zr zTO6c%&FfLIkYol_GM@l`0+3wnQ32~!l8%s>I0an^6Q6#acK|O z=UiS5twdhKLb2GQ)JCS`Pfb!6=A~xtgo;YDyMwFMz{ER&@Y~i-U%^yHi}26fHWQe( zOi(lp)qRAMq?MFDrSpdT^k!{a<9E*1hE2ehcW1EpYr6$3ZK2i^N9hmdrX-;8cP^fpW8kPn=xO|=X z@npZoouz4MUijz~69f_%7fqNznv5%pjc3aao7`!hcp8^VIS`LdTQZ*ZZ z7s>SVxtoD~IpHFdd z65Z<4SgKj3hs(B&3x-T1>hFG?YSGD_R;n+w+i3GBo*Hq`QVmC~n)e=4^2d%kADw{sg%!o@%voK%o2Ua$D6oQIufxEV2jb~v|EOFxMC`}2c9 zKo5e5vv=(X_wcgmGI;kdJr1e$+w1zeLy-L=+k9?GY^D=TG`91Jp36$pF26C$eVs3NFh#~l z?4zVzu@IvFP#Ox9gZefwLtsl*u2QI;WhtT4tYT;w9dg#e!DDD(knoxSWHmI}LH_rg zGe@|=SZ1mIm*0(Vi9;PXQ|9Q#jhgRREk+NDikks!qmfXhu=~Bysn+rC%lfjTjSaKn z=H~@%u*{w`-d`h9DIi5tLiUf1EN{7}?xerhWd3B-#Jc`6+&A&yur}ec2;a6IbdQ}hPQxjGz{KHbt9USdofJX?KL zI)-SE#iy7iW@bhP2B3AWMAZK2tcB&oEnw{mrSM0nSLk1urpY-SO+J9Uas&#la;bkW z%VvEL+SVr#I@L_Mp2GGf?N*ApkMUivCsvrtzDuuw*?h`OPoG3_17~v{Zxc0j!ci^c zlGp1ji19!=JYwP+3McWkc9AXC51T|of@Lzk@GkVOtellH zu2lvb;>O|^RCi8hM=C`ZfM=B{Xd7>4!W4u{9}7A-ZH-%&jOV-(2YaTH^2#_?dlm2& zfeqKI7}>=4B0^g3bAMssQLbl1gPwy=00`FTQp0S2KPhasgg`<1Wxe&W%y|yMn=G+^ zuM!BjrnB?)sBu7}L8KId1w#!2;=eh#hc=uK_0ZN1PnGPHquQRH3{>Gpn8Q^v4@Zpp zcjp}!C!B3BU2BvT8V)jm=5ccrXA4b@XKpaW^T@pJQ+ z{j3t^20`2VdGO+ksa@j-{>?$Um=y z)wg)invw&x{caihs`3g8dl{CWFKk%fIrp5pB&asg4%!62X6HQELn9wNAacTR%JKG| zTW~!CI~4yKtD}mkiK2yT*v0bCD@PL4tBctNh_>C{f*v5>U6(p!m@5*#M2siZH#EQ6{u{|NPZZ&q3#E zBxu;I+=bKn(fg;MPRZtxNBCNz!J5=?luw_J^tW&&Y)9ES@hQRaaR>tvWk^Nx5FxJlH5YaUK9l)2;210 z>Y$BrUI{MGCsvNZP2Dt}i7JN)LtI#&eH@AHu0rE1!J=qte*Ptx4^X?ltLV}6R8&&& zE*b?Nuz`YtCg38>dzTj*8+ZV92USoIaCi5H`$k7sPvUGh7&Srd&DP#JI)A{hT%*#w z)Okh*CD^~m*-e`OJ`T`ptT3P@KHG(zyM5rgdLdU99wHd-ctFcZ^ct)m&wN^xX`@*y zO(7l_5#OZ64rh-;0e_~>*aK$EqDPkDbG-2?j8ihg^)XHAV&t{1=(`?7ohy!8*he-$ zLUn$6obm1u`+B42GV2T9i1Wa z;!F9I!YRtbKf}VuH`n_pptzfdY0Z_wxH*%v5n(hC5upfP;jNMS9Ki#%moOOIf8xc5 z|M5Wm-H(0!<5?|~x~0XOuNXr7>EL5}hVZeju9k-mU3*bj}kVgd}xQKlLH;iTq!9&W-*f>D@ zA@}ePwVcf*#iSso9L`Y+CAEL-Ug}Mebbk~0RHKN-|J=lHdt)SODux)>%}sGg{CkJU zQ3#ga3K-=8f9J*q-H8TEFabPe37pSvl%>UEbcYwpyee27Y;tN3#L{CPPsL?w~5H)&irz4tm8qJ${X zdyigcg3;T5^F8<8-@X5O|MPgBF~`{F?7h#~`(10j?^+J8_4QNOQ-q<1jmyImDlh!8 zma~ukW(`zn4I~;!bV-^+h8U;b*T{aYkSdTGG-^K?o17dOnOF_DJdi|A+ej1YPVTi3 zIVU+E(YYOz#vdu15h|dRWXcIs6m5Yv=+&&$G}x@Rk~r78V0h=2ltKuW@e5gp*QOF>l7(3#~=fF`H> zVp;6Vk4*fQ9b@T><5M~IIBuKaTT}SGKy1T0#Bc`#n?qU`Jb(01#A^$%0f1GX=6eCA zL#jM%iCjInvHk8oH5!UA$W*4ApK!(Eovrb$rUqsu0sfT;54^7RoZ$UG%)Ec6we0ze zfqYUtl|VO2n^II{dqw`h!dk}ouk`;L3*{ML9D~>YpEm!&%qgMAs*vcxsM6Q*2BRSw)6w#>YItW!iWDh-=w2n11Ys`~p8DeLCa?S^_j!xe*&9 z1&1*@K9?8gT2|y>jHmzD2w#oGzi4tvy3%~DOy4=Y95P7m30@;(+-t(-+nSXO54uyy zmz%tH^>wU#I8JCFbfy4xG{o5>IW?ImO`Bu4W=#%nr5>OBvk=lF_xT_7ut#3M%Nq5K zU>|PXs-ldMst8n=UnaO-n31E$o00s&`G1;A!&&Y}a|tdgHPP6cFOVe(_x5Hc1b?z7 zMoyKKv#sj}Eft&oW3JlYhkALn zfO2rA-Hfl3L@g(6^1gzAmn~J@J>a75)o-$=GEbPUr`-Yo5m-xr8id#!>oEQr>OTaO zO++eIIw!x48SD3*IZSb>@L|pApt_&yxqDX+h?Y&@IKo1{2gXZB~5v}+_W1E=*3P4EvS%eTK0xSHp3JIiP_#%Hb}&w z{6B>U2YrD+{t@_1!8RCpMU4;6!W3ufUD2M~C$G&SK)#a3#$YBkQSXyNcl;E6>2Np^ zE&MwV`-r;opH><>al!h!nOc|C{)oktWY4;Tjn&0XgW#F>6luoTr_<97l^AD89ZWpF zZxwsGUP%)8ok4g3upILF8h~AoN6zy+|C!ih&LtHKz}foP_M~$c1c`2AA7QqBms_lN z)u8jymx~2sh4v%mQeQ9G5b^+9ZjV=aF4oNfycbH&n;ewg8*go86d4hr-9@UHD%MK& zx1D~yg^dZOubD3Ei<~RVazRvN;>spjZf)iQ@aPun3!QE@2yXJ*8q0N!ZGwexYBml) zJiZbGHP!AXL$@c+?~_urot+(@b{R_CVIE%?yPB4?RYwc6j)zDDl*8E^*`I<(WAoPF z^7V1A$FlI|L|-4B=N&AFQN;u>G(mz_VPV}TL?Fhpw6a1(7~TQO!F6|bK4oTJ*gGbn z9exaMbZQ`5!ZbN~H&1K(7S8;9fFsp1?UsXGuY*;~A3+QwBb} z2^GLUIOgt{s)e$)Hh@B1T&$0>@M(51Abiz=~W@%ZTE5H+Hof3_QAUG?kNy?YoA{hdf1 zfGCArhyNBc_#=Q>ufeJ4Q5_mg(&06$m~f0s6>wRJ6XY(`tuZPS%N8Q*OLg9y$-uB3 z9i4>Jau0rg?mHax7)TGJxtJ%4qDy+&`;Z`Yin^YvgQ#9T*op-NU5FUVmp5JPps66R zu@EbpjZ43c4Xo9r@lLQ2|NC%Lop=I@`O#iWt#_oK1&0(8jj_pz`@L$0Qr@@IVR=r9>g?f%3Wd?m!^j>1pa|YyH~6 zo%6anSIu){2@V&(+l}@0v2XLKlb^QJ0S`{udAj@g-3c zpML5iyEO}9+BWY%_Lx_%7@Vl%$PM8!ATcVc1UynXJ@H8?DXoR^NlE;MgZBWV9)QAF zD<|OSqONm>t!>r49dM)xmzD&5pMqVmX#iPFrY_8@-r03nSu2k90Zu?1viNZCozv#b zUYCY_nle)rsgq`YUY>^6sya>{viYp0e#nV~`*D3q3-lFmTTGj)J2vB$S#=-f<>4ns*a2S>-tn^}ZL0?t`rI86^kD6* zLWYD^@$RXfGmMF-BVXd3-;3);M!EA#vofTEgxr-w=Aof*@plMw^hK&@!yzif#8ZGu z(d(sAS02y4GP8d;eaaimFPO1}1QCIBP7Y1D=uty;xBHdBs)*T|sF0PF z?KvGf!7El~G9o~|RuWB3^$_7bo}g?eC0*IV$`&;qO|B7wP1pMDHwVp7EdDepH*E8DM@Ka~pE9CN zW?gL2gM|~;l9kTKpFxg^qxf@j@&sMjmXV@@a8%<~Aid#`*tz#+SV$Z@OUHDhsf5*T zMChjG55_0$j8sEhQ2G0i6HoL7!KXa-C7IX4bfn@GtqNnIa1=nCHEe1x z!fAB0cWI20Tk(a&L6UZjadg#<`c(~yWei8FgnlEJA&RhrZza5Ba#9p1H> z``f#|b8>ZbhB?^|O$si2e{s;?#k-00zDY;WUZ`ExGdU^8!JmWTnZ;uR(vTlVWwd}y z5Q>L)yqdnUp{YIn`{Rw~7wp@!Z!h)C%&c^v&=-=+B+HP9uhFS)#(jg1tJ)O4DCTIo z%hIhl0%<(+xqy-J3{J!SV09wRCixj;eX61?6|vAp$nB7V`ir-+#nc@}6utQ&5Hwk=49J30x&Hhv!DPA??}=G*6{!9CF_FI2$HInTev z)JxncKleO9U0T>!SerANGj&pN(*z+9PG2Yye(hIE|>DzW#pw!^EJKzPnmq7tRUO(u~CixNJ1Xhusa>X}FwC zE_0ka2MXs5cS~sCG&6SQCJbO9u^LP^{r!d2z*gDZPz{AYa76A~f_(HRW!gAA9ndwEy9+7^<92jLAHBU&mEC8Pt7 z7$qA!yR1m8*Krqb_C2uPx$tlX&wPArGr6>v6Lq*Xxv@MU#Q}T8aXm-d+cqE>QgBe~ z@4s(xKqJyGP8(8EY!8s0lF=#N;m7nJy%$2MGnRWRz|*9`Bs~jzWOdPEFXVor06%u$ z4UST4H7hu@P(8sn*M=lDxvgnzid4^fZUJ!+5$BnAZw#-;d7O-%#wX^&AI@CMI2B5v`- zo@+k=ZI2&$v9On*RiZEckh;M1U4bSWH)=Y|SIds6F9=MDB-4Yd)d?W^vddfOX~fXP z#Kg=D5`1<5uQ(jgP*c9{Ogm-B2-)tkWe2n$zi0fmTcA&evY|}ZrvWN zaP|~q0_x7L#vCur!Qklh^o1EuNUdJ6+^Rg|~i9a`^*g zfhJrNWk(j-_khFb4HS6o+v`VG`W~J}+)0`ON9;E_IG>3!i)OdOeK7nM)PL zZ*SAD_laU-zj=Z=X|ff@4f$NXeNuhYIC@novgInTe-5m!a_of;fdH5!yvfcQ19-7!kF!uf#iUXp6#GU_ltU|Ctxcp zzwls=&JXzd)pKPL!om#1>b)I71!y_kztp#=A+~6NO01i@3 z@{+QsAR{9J?Ar$_J`I6o=;zTeeh&HWl^*bP)&p>Oil2+>9r$L^K&cbv!{h4gE+sWr z6F6P#A}nGviM!Ybr<@%bT3nPJ^76#yt}*Hf%m6p+hm$6jsP2~%uPj95yTik?c2=RV zjk`_nAp7;s&xERB{onlu%5ceg3_XTRJZ4ubzSK@XmXJ>Tb38)NZ7CP;J9lGZ;tKapzNRqiVha#nhr3E`NEKm6gfL1r;_ z*&$tXb68jup|X-M)Zw6D8;bwWO#Dq>U0q#HQ93cI!(Upsll7H!>0qaIn^)%7iktC0 zqjicFgl*S_Qu5gjaJiuye^Lv>=ISrMx$l|zh@H|B@j=5s`K!MrF`S4w1Ex}%nqPMB(9lr-)p?$?@eWW>EaUo-9?^p^9+cCg zf&33?=p zNF}Pv^|;=Fj<-RP)nHu$Xi{&aZ!+l2a=71X4E3S-JQP}? zy1t4d`4WADA;nYvLQvCz;T7%rwU>@ZKxnMLooy=N0B?lLFE2A#f+R=f)zwM9v&gJc z=$!n~{GDj|E2qevs7zJap%sC2Xr|NdrN$Tb6MW+BCST%y?PlS5;4&H4Iym~%Ix{zg zjPK`F1AYvd_{$uDUGOe;YG#(M2qzE+6yHRROb#9!+oydG2G2v!0s*w%XE9q$^?F_` z?t^Z5V)8a7wo)afx;jD6Bd);ArEv^P*jEwJ4G%7f}1qK$M6x+TeO ztHYTz7bC_Nx5lD>Q|LaqH|CA1#m^uYNz39lX7t16<#(#QS5YzIm*XSd#+_pZc5{gO z6-L)x%tV8!t_z2Om_OrH7yHLYcD>gIy;+oqQy3szLQb`jL4yR{!9TY|t;|h5SS~He zJaJR~lqAP?4I=tjfm4aaeYdS;I@yv^2)JfJm_A+zRNEOq1pYW!VOUTNuKF6Mt2xr$ z{1UD9uniRC2@I#IN7ekI{X!uOAb~{A^}Ti{ z_9UMNs58^2cOcwpUng(OaKCNMX4^;*@xZv9F{(9q-{e1U4Aoyx6Y+F#=v>_+<+@81 z^u?@K6-u1$HOj#<7Ix|OIo!AZvqqCu!xhKJuU)Z^xALWG_MG1xXCBV=wX#LH;+f0~ z%)z%beWb`rZ)&HHxqPRNF2d|wSmF-N=dia>cKycm{Cs9HGhdLZVwAc*zE?N_wwQj9 zPhe&{SN@py_-DII!2&VSB5@dJ3NTihpaS*-9}v>SQDO8ckJ6XLOS(Dhrhb`q#3y#8Sr$YrGsn}n&{)wP4`;}Virg0;b9X^}sxSC>1r})qX!L@aOU_#%Z-muLN#SU~OIS*dP1;Kup2dzjP^9{3(I= zPeQNh0?@mnbsW zldma;g8L-V=i)EIr%Rt5j9;>6!yDp#9QQw)_PD7<1X2qFeol5|Y?a)oR`mV1D)U30&x%eaZ8o~XFGCaAanzE8+GhtQ*gEI1-!G&CgD zRd(P=+|bZ33Gr7cn=Aiqa(qQGEQ=}4H%D#9F#~ORSC5mBiAm8S1{X&`MueSI9GnpV z{hIRftyz#GC?O%ihs&_=W!lf5Kc7JU1Ho|yb-mT}@p*5Ho3v%sYvi&vPwI<#Gd+-P-zk8bz6&J`MqZL3c1TqIExLWkFi|@NwU-9g8lT8?us$VfdYlim@W@>5l&i z491z0v2|WO+dQhdX$2&F)^=bC6Z~!H)M9Qocf7h!$!|?&zRO6vze#yt2t`bL`3ZG`&Y7_b!;w#n9##?%u}hY933Z6333uTwY2W; z;@-JOZU$(;BICcR&g1V!!Or&&&k(=E@QeO!gdOyRJst`LQv8EJJS~T)gT6HS?2*<7 z&VY*_vmI~PU8Ob9`Rk6u=0VD#^hfsdi=>*<{UZI3z(r~}#Gv$0{8dx%dsh_|6-ge8 z{znpwVEyk`AHJw)G_YmUo%D1Uz1(gA{*&EqtwCV+mczAl;*&gKdtA=!VA!zmD6A}g z)#YpSWJtVv_#`Us?NM$StfJga1bXVa?W2FL22kR?l&fM(&;379P+0U87oFg^aGqy2 zzP#FWdEU5XUk65>^XL8(D|7azxUPip-#IWODQ-+J3epWfeXh2;zHYhp05ttA7T}7| z-Rs@d3eWnV($@c-)?Fs2dIyK*3PV|%5TWi<_%^Fi!dlF@P9epqY>3aI@mJSBPomyU zF|&f)D|0p)+Aji3q1N&^b2Hd*%th6I3M3bEgrXOvi?*9{0o-fowQhM z!O1W9CCpv*RSvQ{RTW*gc?dN~S4c8xo2ndw3)9a+!^t-2WdHegCLaJmRSx{5pKrPL z#?l~hdg(2xjMvvWtk(5vrthuPsD;OWGO(^#s?SoGYHh}xWbBqR3lbhz@xm%d$XUg& zT6vZ`SrW)cnjl1QSvm5Zb9tdY3s%gT^6D=&LwUwDeQV>0yi_h8KUv>7Ly9Kf{#aWM z9IO2)kRVa@DPfUs6Q0#ordmIS0>!=uL=j~_hZ}6Mm(4*UQ}BY?y39=Vf*fQvhUwFb zX7rV$qg6cote#FZd-O{V6V-^|h?LuK%cR{xEUu+AI9jF*aeoOGwMlp- zOnGDK!1)g7c8icK73sab-BUsE=w>qxUOc0o7IKk*%Yqxec1k##N0* zs0960a$jX(m;5waNf9*~ErcIK`gl*v0E&+f0$nUhYZCK$Y~S;#Fi)QYw9p{8vFwDi*OkTh66!6<_vp5$`;@4y=5sGUPdgQjn3pQ2 z%D@)TEIe@;98Q?=I7kpY#m!UiV%>X#FcTeOVr5RG;G3G_f42}_nx1a75*-k7xk@YG zXL(ufxP26!b997V^k$!hQq3K!qSj~()#Q0{h)W|7O`bs$%j^`~v%(iON6{`1( z%TwgVJUk>VO9lcAQbDoV00o2!Aasvuw?zO@IVb^G8-B)*_PjrT+dA^H{ge{|Y87{%h}??c8H30-ht`+CQ6ZvZf!p3ng+H z#6pafq=$OL(%)~xbayLom~|P&QoSL*TyYZ8AE zdAGmV!{?_P8m4Nv;}y|%(3ig}T*iGGMGK3HKHZ}M3YKE17VUSp#rHpv87H36EJ-K?yGz~zW`NO?9oP~n&JU2PGwm&%?av0LNC<^ z<>gW%zdQ(L3JaPoG|~^I38hJqTI^IqbneYvqA>Z!dMy;ldU^>RABXy9C8I1tA79=e+&T7wM0T4nOOrVMsO5aDFoC@GU#Vlf zq^*qUj3JTgGQga_pG)wz@Lz$$OBX|O1J*aJaov~W)xkjzE}=2)SJMJA*ekCOJTypA zU^dRTTM>>swYenTpUQHyyt-%(HDqBRDfyWE8wr*B(hy>5Zz>wb3st?}7fJ)q#A=2k z;ieGX;`udOQZcwM`r;-+Pf?OaU#NpdC^E*oq^?<(Q59J6Fm&*|BLbSLpHFE99J8Gt z%Wwi9baZN-<7a1oO_B0VT#9Bo#3m|iPgQg@$Jbhq*_SVudw4Bxn(nU+uwBR)L7_fv z7ycqNCAwOD6ws@E<$Je!juC&sn@BD8l<5gYgp;FrJy=vU%QEePc^@5qrl!@XB^=23 zj348uCOwcKu^^SDYOS`&rTA+$P}6{*$gbsRz#)*><2Hsd-QB4*g^K!9C>8a65dj8v zsCQ|S^(iMY1Nz_n9U5d6bKehV-F>5BS|#(}o^KmUNW5yJ7D_MYlUQWsP4Kvw{!^Sv zL#vdyG9~Ob_ns+qC#EEh7rm!#t0PR{Vc&)lCG2iASm@6;#pAfLW#>&7YF#n8KP;)q zZ23sVbOj%AtuS*;Df>KS~Bp6d+oItc*;o<(S8pRUwIXHe)b20)9{c~dC>|vohroy^i>CF zY9IDAg2L@c^D->x=<@AFoOb3MzI z5f*v#hLx3l9c+%M6+ScD88~g7lZI-%uGg{+*z!E&2LE_emRk%dywlOs(aifgFs8^? zT|8=wsUf`v02EO=#mjET_iAHretX}l11RQxaa%Ey*NP3SdHUzTX8>N`D2My6-m&Ul z{68N^((zF=G>%J6wOf1eEpHEl;c>pAvnb2IuKdLAeaX^`;grDAh*d6aGENtis4Uo)#lpYqA-OC5@U&g*V-<<#o9slKl1O?;X zZ=>BAXs+!tA2xziPrX0ta*`D%!Wo}H)=4aja_8c-BGx2bZJklC20iw^U)?P1?&+J? z@&YWps9s)7&$`+z28Kik2@#=e63d~iX*m$-ld5%l5)>jSBKJ5P7xZ?L<@jUg=B{**kiK+Tpb7%&IBGe5y z@l34;C9(~zo6V<|q_;dQSk$1Wv1Ysk@uXzdmeyJtPh~?x$OY^^H0~9J_OQeWxuVnN zBCbaB^+%e6>b-JaUGn~wb63p^eA0ryCh={}>A8G(YkC&nbrzEcD?AWNp^xhx!te0F zK0Lhlw~#lJA|QdC58XV+KdZJ|?pb&G)Ig1JK*lPLVxBiq%Qq$QJ3k|gAtftT0VTD;7F@*jS>MSWDb!c7-0EidrsI~=Cb4!p1DvkljEMff`s5! zA9r>S4HcDG?%YZVnOs5sur${|YHYO4@tl^z_sZIbe8``nn`6BxBx0rL_L7zEOw}0o ztI>htxv;su{vyB!NqvewKA9gaN#G${YtXBG;ZR1*X;5-Ebi7&tI?)RqZI2bISur6a zjn?Y4dbs`SWTK@byx}kZx&u6BH`YdX_VeSkz14OOJrTE-%p>v3TF?D*(7}wY!XLh8 zQj&z$z?-fi@48JghxByrvx9sdFU|h5lM&E_E-fwBRH6g@n6)Q3@))Cm=%jpAkd$Ch zFrnx&G%q@FIc}C1ioI}rygnX~xwt_`PS{l3+vs529J`I2qpjY{p`|r7=RDUMR1D}z z6yB}q<4BchbY_+^g@XS1X-Vi?TPW#rJ2eh`SSrjsrPrp9v>!rk*T>9NCn$4tl79|C z1|4&eQE-O_yf|2hG{Rq<5bh_yf$P=L@fA$iScdZnzg4eder>pFLSxG9diNi2iWQP)^+uc7vJeOvdF~gkJBb z?X|_R>CuBCsCN((`nNw>FD~d-|HE}d(aI=9r^O*DBW>)NyXe@AoC%8+23Ih>ca;CNdd^ChVnx8c!2=U3sQhjJ{q^7JBU*=%yrB5Y3)(T*SVAH0w}79{9S z6(LMWB94fNASBd~eInV$?S7D)-mVz?{AlZtI6#k3z-YLhIHxo)lPEZXGdw6>>H%}M zSCfd*%!WpVSG!)D0yI~CNNZKg=Rs!o*NP%Z@7}OS zc^|@-Ha;$m-0d_#Pt!%o^)XPHg+XP7HFWg6Ada+g-QF2{x;jE5$k{SA52KdF)5CEZ zX|l1s&JH;o9L0`~yl}F_goD#}l`qroo6piijK#$HS0C34B<*LGeoLr&jXGb|&9k6d zpRC#p$TX%uK4vt|_~I@S$4)b{oX2$q)xWcRE2g(ElWZ-YZ~6SHi8t`o*lss2bEc53 zh$Ay!`;XS4hT{s`3O3tqz1dv>lGClnB7yvRJ_&-n>(AT%tivZNZL+e|W=hGge%o(t zbHEjIJG5ybgiwgtW)sGpHaXBbBJ>~&56{huI@48J8>wk08$-Im!g zwErqMJ}V(E5EuW|drxR8!(pj3kK2|yrIDP>5#o4x#L-k;ZLh7L%Di{wer@;*1QrUs zu~jlm=e#q@#Vc>m<@zSANIL7BwarHR=G4ACtNbi_;xXaR--W&`^^&3{uDXgq^#3`q z+NR>jUnhS5DM!1P@iI~Z_ma)!Ap3?$z+1T6?WPG)0=`8 zCl^L_|48-zYm*3jBgU5-De6H=nK*di>&DLsd zR9)}ss=6FANmpNTOavmkIaY(dN}=d6C*MNjy2-MUxna*Dpzd;#^0tLa`@|m&ES_q& zw>N0oPI;f4t5%F9k_*vYH+HqXiN~=wnHj2R>-C)0_^rGXXqS_>U&aYgIPl7ne5%sE z*x{m7`FMWMUC?n*>qb%eVtH&Y#mPd=+~(J7x!Q0vXd_JDHxKj2t=)eP)!o?Ox_D5P zRI{0QUGe3)L{?zYS|J--jKQv(#~b5oxY7T1q;d8f6Sb51h>niEB8sBBxp`o&Pm4K* zaG>tB?ndGxaCunIZvyxEiKJXU&EC~VEDKiP%r~g>cpdJ3{h5cfWJTKP+NC^9`(N=( z5LfI?oUGKzfx?&@!4R_VNzj9u5)mCIJReBDa_KUO&w*37qSM1b{wIB+Ij YEJvCFouLIHRiIs!Qj{!y`S!#A0Gf+m3;+NC literal 0 HcmV?d00001 diff --git a/docs/guid_from_oauth_endpoint.png b/docs/guid_from_oauth_endpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..310f5cba9ee4db3e7b4fc703be5b44fc6c7fc1d7 GIT binary patch literal 51214 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

Qs-q&W2*lM#)5R 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 UbRz;FEd(O>ToGC%`ReWe1MG#w6951J literal 0 HcmV?d00001 diff --git a/docs/key_generation_copy_me.png b/docs/key_generation_copy_me.png new file mode 100644 index 0000000000000000000000000000000000000000..d95a3763c855a15d77b78a894b83ae3e8ec8c709 GIT binary patch literal 68431 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

qD^J_VZ={HOFXk{%@MQ5`2 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

3c0-za(iwu8w+nB=&W@AWcBd)sTDP}yP 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 literal 0 HcmV?d00001 diff --git a/docs/research.md b/docs/research.md new file mode 100644 index 0000000..4bf6085 --- /dev/null +++ b/docs/research.md @@ -0,0 +1,61 @@ +- [Azure Equivalence for Existing EC2](#sec-1) + - [Locating the Equivalent CentOS Image](#sec-1-1) +- [Summary](#sec-2) + +The [Terraform Azure Provider](https://www.terraform.io/docs/providers/azure/) needs a publishsettings file + +# Azure Equivalence for Existing EC2 + +## Locating the Equivalent CentOS Image + +Currently the cncf/demo [uses the latest CentOS7 AMI](https://github.com/cncf/demo/commit/62b4ee750cea96ac18d9998cebed36660b3d5864#diff-165521d9e758a5a743ea42c6fd528156R10), so let's go find that at Azure. + +```shell +az vm image list -o table +``` + +| Offer | Publisher | Sku | Urn | UrnAlias | Version | +| ------------- | ---------------------- | ------------------ | -------------------------------------------------------------- | ------------------- | --------- | +| WindowsServer | MicrosoftWindowsServer | 2016-Datacenter | MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest | Win2016Datacenter | latest | +| WindowsServer | MicrosoftWindowsServer | 2012-R2-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest | Win2012R2Datacenter | latest | +| WindowsServer | MicrosoftWindowsServer | 2008-R2-SP1 | MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest | Win2008R2SP1 | latest | +| WindowsServer | MicrosoftWindowsServer | 2012-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest | Win2012Datacenter | latest | +| UbuntuServer | Canonical | 14.04.4-LTS | Canonical:UbuntuServer:14.04.4-LTS:latest | UbuntuLTS | latest | +| CentOS | OpenLogic | 7.2 | OpenLogic:CentOS:7.2:latest | CentOS | latest | +| openSUSE | SUSE | 13.2 | SUSE:openSUSE:13.2:latest | openSUSE | latest | +| RHEL | RedHat | 7.2 | RedHat:RHEL:7.2:latest | RHEL | latest | +| SLES | SUSE | 12-SP1 | SUSE:SLES:12-SP1:latest | SLES | latest | +| Debian | credativ | 8 | credativ:Debian:8:latest | Debian | latest | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:latest | CoreOS | latest | + +We are looking for CentOS, and looks like the default Publisher is OpenLogic. Let's see what SKU's they have in westus. + +```shell +az vm image list-skus -o table --publisher OpenLogic --location westus --offer CentOS +``` + +| Location | Name | +| ---------- | ------ | +| westus | 6.5 | +| westus | 6.6 | +| westus | 6.7 | +| westus | 6.8 | +| westus | 7.0 | +| westus | 7.1 | +| westus | 7.2 | +| westus | 7.2n | +| westus | 7.3 | + +I prefer not to use 'latest' anywhere, let's grab the precise version of CentOS 7.3. + +```shell +az vm image list --offer centos -o table --publisher openlogic --sku 7.3 --all +``` + +| Offer | Publisher | Sku | Urn | Version | +| ------- | ----------- | ----- | --------------------------------- | ------------ | +| CentOS | OpenLogic | 7.3 | OpenLogic:CentOS:7.3:7.3.20161221 | 7.3.20161221 | + +I'm comfortable using this image as our base. + +# Summary diff --git a/docs/research.org b/docs/research.org new file mode 100644 index 0000000..a5b2eae --- /dev/null +++ b/docs/research.org @@ -0,0 +1,281 @@ +# -*- org-use-property-inheritance: t; -*- +#+TITLE: Azure Support +#+AUTHOR: Hippie Hacker +#+EMAIL: hh@ii.coop +#+CREATOR: ii.coop +#+DATE: March 1st, 2017 +#+PROPERTY: header-args :dir ".." +#+NOTPROPERTY: header-args:shell :prologue ". .env_prod ; . ~/.rvm/scripts/rvm" +#+PROPERTY: header-args:shell :session none :exports both :cache yes + +* Azure Equivalence for Existing EC2 +Eventually we will +** Locating the Equivalent CentOS Image + +Currently the cncf/demo [[https://github.com/cncf/demo/commit/62b4ee750cea96ac18d9998cebed36660b3d5864#diff-165521d9e758a5a743ea42c6fd528156R10][uses the latest CentOS7 AMI]], so let's go find that at Azure. + +#+NAME:list azure default images +#+BEGIN_SRC shell +az vm image list -o table +#+END_SRC + +#+RESULTS[f42a28cef38b0b7c3346005ad6b0128d06d069b1]: list azure default images +| Offer | Publisher | Sku | Urn | UrnAlias | Version | +| ------------- | ---------------------- | ------------------ | -------------------------------------------------------------- | ------------------- | --------- | +| WindowsServer | MicrosoftWindowsServer | 2016-Datacenter | MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest | Win2016Datacenter | latest | +| WindowsServer | MicrosoftWindowsServer | 2012-R2-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest | Win2012R2Datacenter | latest | +| WindowsServer | MicrosoftWindowsServer | 2008-R2-SP1 | MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest | Win2008R2SP1 | latest | +| WindowsServer | MicrosoftWindowsServer | 2012-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest | Win2012Datacenter | latest | +| UbuntuServer | Canonical | 14.04.4-LTS | Canonical:UbuntuServer:14.04.4-LTS:latest | UbuntuLTS | latest | +| CentOS | OpenLogic | 7.2 | OpenLogic:CentOS:7.2:latest | CentOS | latest | +| openSUSE | SUSE | 13.2 | SUSE:openSUSE:13.2:latest | openSUSE | latest | +| RHEL | RedHat | 7.2 | RedHat:RHEL:7.2:latest | RHEL | latest | +| SLES | SUSE | 12-SP1 | SUSE:SLES:12-SP1:latest | SLES | latest | +| Debian | credativ | 8 | credativ:Debian:8:latest | Debian | latest | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:latest | CoreOS | latest | + +We are looking for CentOS, and looks like the default Publisher is OpenLogic. Let's see what SKU's they have in westus. + +#+NAME: list OpenLogic CentOS sku offers +#+BEGIN_SRC shell +az vm image list-skus -o table --publisher OpenLogic --location westus --offer CentOS +#+END_SRC + +#+RESULTS[bd6ed1e6f2fc3c614c736373beafd146068045f4]: list OpenLogic CentOS sku offers +| Location | Name | +| ---------- | ------ | +| westus | 6.5 | +| westus | 6.6 | +| westus | 6.7 | +| westus | 6.8 | +| westus | 7.0 | +| westus | 7.1 | +| westus | 7.2 | +| westus | 7.2n | +| westus | 7.3 | + +I prefer not to use 'latest' anywhere, let's grab the precise version of CentOS 7.3. + +#+NAME: list OpenLogic CentOS 7.3 Versions +#+BEGIN_SRC shell +az vm image list --offer centos -o table --publisher openlogic --sku 7.3 --all +#+END_SRC + +#+RESULTS[7a0228f7fd750965697c45a561145ee0cb58e207]: list OpenLogic CentOS 7.3 Versions +| Offer | Publisher | Sku | Urn | Version | +| ------- | ----------- | ----- | --------------------------------- | ------------ | +| CentOS | OpenLogic | 7.3 | OpenLogic:CentOS:7.3:7.3.20161221 | 7.3.20161221 | + +I'm comfortable using this image as our base. + +** +* Communicating to Azure from Terraform + +The [[https://github.com/Azure/azure-cli][Azure-CLI]] can be utilized to create creds for terraform. + +Let's [[https://azure.microsoft.com/en-us/blog/run-azure-cli-as-a-docker-container-avoid-installation-and-setup/][run Azure CLI as a Docker Container]] +Be sure you have a Paid account, the free trail quotas are too low. + +#+NAME: azure account list +#+BEGIN_SRC shell +$ docker run -v $(pwd)/.azure:/root/.azure -it azuresdk/azure-cli-python az login +To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GLKE7GL6F to authenticate. +[ + { + "cloudName": "AzureCloud", + "id": "5358e673-95e7-4cd8-9791-ca28dd5e3cbb", + "isDefault": true, + "name": "Free Trial", + "state": "Enabled", + "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", + "user": { + "name": "azure@ii.coop", + "type": "user" + } + }, + { + "cloudName": "AzureCloud", + "id": "70693672-7c0d-485f-ac08-06d458c80f0e", + "isDefault": false, + "name": "Pay-As-You-Go", + "state": "Enabled", + "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", + "user": { + "name": "azure@ii.coop", + "type": "user" + } + } +] +#+END_SRC + +#+NAME: az account list +#+BEGIN_SRC shell +#az account list +#echo HELLO +# docker run -v $(pwd)/.azure:/root/.azure -it azuresdk/azure-cli-python az account list | jq ".[] | select(.name==\"Pay-As-You-Go\") | .id" +#+END_SRC + +#+RESULTS[4e9dccc24c6d7d7b761fe504e1cc41f7d41c7914]: az account list + +#+NAME: generate azure creds +#+BEGIN_SRC shell +$ docker run -v $(pwd)/.azure-cli-creds:/root -it azuresdk/azure-cli-python az login +To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GLKE7GL6F to authenticate. +[ + { + "cloudName": "AzureCloud", + "id": "5358e673-95e7-4cd8-9791-ca28dd5e3cbb", + "isDefault": true, + "name": "Free Trial", + "state": "Enabled", + "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", + "user": { + "name": "azure@ii.coop", + "type": "user" + } + } +] +#+END_SRC + +Please note that to run this demo full, you'll need to have a pay-as-you-go account due to a resource limits. + +#+BEGIN_SRC shell +ARM_SUBSCRIPTION_ID=$(az account list | jq '. | map([.id] | join("\n")) | join("\n")' | sed 's/"//g') +az account set --subscription="${ARM_SUBSCRIPTION_ID}" +az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${ARM_SUBSCRIPTION_ID}" | tee servicePrincipalProfile.json +ARM_CLIENT_ID=$(jq -r .appId servicePrincipalProfile.json) +ARM_CLIENT_SECRET=$(jq -r .password servicePrincipalProfile.json) +ARM_TENANT_ID=$(jq -r .tenant servicePrincipalProfile.json) +#+END_SRC + +#+NAME: list my lb +#+BEGIN_SRC shell :format js +az network lb list --output table +#+END_SRC + +#+RESULTS[50522849401e494e714a1f3cb413e3a936dd0cb3]: list my lb +| Location | Name | ProvisioningState | ResourceGroup | ResourceGuid | +| ---------- | ---------------- | ------------------- | --------------- | ------------------------------------ | +| westus | TestLoadBalancer | Succeeded | deploy | 93247839-cdcd-4d24-b461-acafafc59833 | + +#+NAME: my my lb +#+BEGIN_SRC shell :format js +az network lb show --name TestLoadBalancer --resource-group test +#az network lb show TestLoadBalancer +#+END_SRC + +#+NAME: list resource group +#+BEGIN_SRC shell +az group list -o table +#+END_SRC + +#+RESULTS[e1c878abc4d0ba479f12a7c7ae192f70a58feec1]: list resource group +| Name | Location | Status | +| ------ | ---------- | --------- | +| deploy | westus | Succeeded | + +#+NAME: my my lb +#+BEGIN_SRC shell :format js +az network lb show --name TestLoadBalancer --resource-group deploy -o table +#az network lb show TestLoadBalancer +#+END_SRC + +#+RESULTS[75c3b3395ca9f70185e880e4c9b14338b7287472]: my my lb +| Location | Name | ProvisioningState | ResourceGroup | ResourceGuid | +| ---------- | ---------------- | ------------------- | --------------- | ------------------------------------ | +| westus | TestLoadBalancer | Succeeded | deploy | 93247839-cdcd-4d24-b461-acafafc59833 | + + +#+BEGIN_SRC + +openssl x509 -in .cfssl/k8s-admin.pem -noout -text +openssl x509 -in .cfssl/k8s-apiserver.pem -noout -text + + +#+END_SRC + +#+BEGIN_SRC shell +az group delete --name deploy --yes +#+END_SRC + +** CoreOS + +We might start with CoreOS to focus on getting cloud up now, then look at images. + +#+NAME: list CoreOS sku offers +#+BEGIN_SRC shell +az vm image list-skus -o table --publisher CoreOS --location westus --offer CoreOS +#+END_SRC + +#+RESULTS[1756d18b49a91fd7d620f538082e7101545e22d7]: list CoreOS sku offers +| Location | Name | +| ---------- | ------ | +| westus | Alpha | +| westus | Beta | +| westus | Stable | + +#+NAME: list CoreOS Stable Versions +#+BEGIN_SRC shell +az vm image list --offer CoreOS -o table --publisher CoreOS --sku Stable --all +#+END_SRC + +#+RESULTS[508f2fcde4974eced346be39c6788c31c21e5230]: list CoreOS Stable Versions +| Offer | Publisher | Sku | Urn | Version | +| ------- | ----------- | ------ | ------------------------------ | --------- | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1010.5.0 | 1010.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1010.6.0 | 1010.6.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1068.6.0 | 1068.6.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1068.8.0 | 1068.8.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1068.9.0 | 1068.9.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1122.2.0 | 1122.2.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1122.3.0 | 1122.3.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1185.3.0 | 1185.3.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1185.5.0 | 1185.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.12.0 | 1235.12.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.5.0 | 1235.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.6.0 | 1235.6.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.8.0 | 1235.8.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.9.0 | 1235.9.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1298.5.0 | 1298.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1298.6.0 | 1298.6.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:494.3.0 | 494.3.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:494.4.0 | 494.4.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:494.5.0 | 494.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:522.5.0 | 522.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:522.6.0 | 522.6.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:557.2.0 | 557.2.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:607.0.0 | 607.0.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:633.1.0 | 633.1.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:647.0.0 | 647.0.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:647.2.0 | 647.2.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:681.0.0 | 681.0.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:681.1.0 | 681.1.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:681.2.0 | 681.2.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:717.1.0 | 717.1.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:717.3.0 | 717.3.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:723.3.0 | 723.3.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:766.3.0 | 766.3.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:766.4.0 | 766.4.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:766.5.0 | 766.5.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.10.0 | 835.10.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.11.0 | 835.11.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.12.0 | 835.12.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.13.0 | 835.13.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.8.0 | 835.8.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.9.0 | 835.9.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:899.13.0 | 899.13.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:899.15.0 | 899.15.0 | +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:899.17.0 | 899.17.0 | + +I think we want: + +| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1298.5.0 | 1298.5.0 | + +* Summary + +# Local Variables: +# eval: (require (quote ob-shell)) +# eval: (require (quote ob-lisp)) +# eval: (org-babel-do-load-languages 'org-babel-load-languages '((js . t) (shell . t))) +# eval: (setenv "PATH" (concat (concat (getenv "HOME") "/bin:") (getenv "PATH") )) +# End: diff --git a/docs/web_api_application_type.png b/docs/web_api_application_type.png new file mode 100644 index 0000000000000000000000000000000000000000..3fdfae5ff7539bec9d055da5dd58afdf03708557 GIT binary patch literal 34235 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 literal 0 HcmV?d00001 diff --git a/init-cfssl b/init-cfssl new file mode 100755 index 0000000..e880777 --- /dev/null +++ b/init-cfssl @@ -0,0 +1,109 @@ +#!/bin/bash -e + +function usage { cat <ca-csr.json +echo "$(ca-config)" >ca-config.json + +# generate ca +cfssl gencert -initca ca-csr.json | cfssljson -bare ca - +_chmod ca + +# generate keys and certs +generate k8s-admin client-server "${DEFAULT_HOSTS}" +generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},master.${INTERNAL_TLD},*.${REGION}.cloudapp.azure.com" +generate k8s-etcd client-server "etcd.${INTERNAL_TLD},etcd1.${INTERNAL_TLD},etcd2.${INTERNAL_TLD},etcd3.${INTERNAL_TLD}" +generate k8s-worker client "${DEFAULT_HOSTS}" + +# TODO: fix cert provisioning hacks +tar -rf k8s-apiserver.tar k8s-etcd.pem k8s-etcd-key.pem +tar -rf k8s-worker.tar ca.pem diff --git a/input.tf b/input.tf new file mode 100644 index 0000000..b47ad99 --- /dev/null +++ b/input.tf @@ -0,0 +1,24 @@ +variable "name" { default = "azure" } +variable "internal-tld" { default = "cncf.demo" } +variable "master-node-count" { default = "3" } +variable "worker-node-count" { default = "3" } +variable "master-vm-size" { default = "Standard_A2" } +variable "worker-vm-size" { default = "Standard_A2" } +variable "bastion-vm-size" { default = "Standard_A2" } +# Set from https://quay.io/repository/coreos/hyperkube?tab=tags +variable "kubelet-image-url" { default = "quay.io/coreos/hyperkube"} +variable "kubelet-image-tag" { default = "v1.4.7_coreos.0"} +variable "image-publisher" { default = "CoreOS" } +variable "image-offer" { default = "CoreOS" } +variable "image-sku" { default = "Stable" } +variable "image-version" { default = "1298.6.0" } +variable "location" { default = "westus" } +variable "cluster-domain" { default = "cluster.local" } +variable "admin-username" { default = "cncf"} +variable "vpc-cidr" { default = "10.0.0.0/16" } +variable "pod-cidr" { default = "10.2.0.0/16" } +variable "service-cidr" { default = "10.3.0.0/24" } +variable "k8s-service-ip" { default = "10.3.0.1" } +variable "dns-service-ip" { default = "10.3.0.10" } +variable "allow-ssh-cidr" { default = "0.0.0.0/0" } +variable "data-dir" { default = "/cncf/data" } diff --git a/keypair.tf b/keypair.tf new file mode 100644 index 0000000..45f3c01 --- /dev/null +++ b/keypair.tf @@ -0,0 +1,22 @@ +#Create SSH Keypair +resource "null_resource" "sshkey_gen" { + + provisioner "local-exec" { + command = < ${ var.name-servers-file }.${ count.index }.ip +EOF + } +} + +resource "null_resource" "dns_gen" { + depends_on = [ "null_resource.dns_dig" ] + provisioner "local-exec" { + # filename = "/dev/null" + command = < ${ var.name-servers-file} +EOF + } +} + +resource "azurerm_dns_a_record" "A-etcd" { + name = "etcd" + zone_name = "${azurerm_dns_zone.cncf.name}" + resource_group_name = "${ var.name }" + ttl = "300" + records = [ + "${ var.master-ips }" + ] +} + +resource "azurerm_dns_a_record" "A-etcds" { + count = "${ length(var.master-ips) }" + + name = "etcd${ count.index+1 }" + zone_name = "${azurerm_dns_zone.cncf.name}" + resource_group_name = "${ var.name }" + ttl = "300" + records = [ + "${ element(var.master-ips, count.index) }" + ] +} + +resource "azurerm_dns_a_record" "A-master" { + name = "master" + zone_name = "${azurerm_dns_zone.cncf.name}" + resource_group_name = "${ var.name }" + ttl = "300" + records = [ "${ var.master-ips }" ] +} + +resource "azurerm_dns_a_record" "A-masters" { + count = "${ length(var.master-ips) }" + name = "master${ count.index+1 }" + zone_name = "${azurerm_dns_zone.cncf.name}" + resource_group_name = "${ var.name }" + ttl = "300" + records = [ + "${ element(var.master-ips, count.index) }" + ] +} + +resource "azurerm_dns_srv_record" "etcd-client-tcp" { + name = "_etcd-client._tcp" + zone_name = "${azurerm_dns_zone.cncf.name}" + resource_group_name = "${ var.name }" + ttl = "300" + + record { + priority = 0 + weight = 0 + port = 2379 + target = "etcd1.${ var.internal-tld }" + } + + record { + priority = 0 + weight = 0 + port = 2379 + target = "etcd2.${ var.internal-tld }" + } + + record { + priority = 0 + weight = 0 + port = 2379 + target = "etcd3.${ var.internal-tld }" + } + +} + +resource "azurerm_dns_srv_record" "etcd-server-tcp" { + name = "_etcd-server-ssl._tcp" + zone_name = "${azurerm_dns_zone.cncf.name}" + resource_group_name = "${ var.name }" + ttl = "300" + + record { + priority = 0 + weight = 0 + port = 2380 + target = "etcd1.${ var.internal-tld }" + } + + record { + priority = 0 + weight = 0 + port = 2380 + target = "etcd2.${ var.internal-tld }" + } + + record { + priority = 0 + weight = 0 + port = 2380 + target = "etcd3.${ var.internal-tld }" + } + +} diff --git a/modules/dns/input.tf b/modules/dns/input.tf new file mode 100644 index 0000000..6d4bb71 --- /dev/null +++ b/modules/dns/input.tf @@ -0,0 +1,6 @@ +# variable "depends-id" {} +variable "internal-tld" {} +variable "name" {} +variable "name-servers-file" { default = "azure_dns_zone"} +variable "master-ips" { type = "list" } +# variable "vpc-id" {} diff --git a/modules/dns/output.tf b/modules/dns/output.tf new file mode 100644 index 0000000..e1809b4 --- /dev/null +++ b/modules/dns/output.tf @@ -0,0 +1,4 @@ +# output "depends-id" { value = "${null_resource.dummy_dependency.id}" } +output "internal-name-servers" { value = "${ azurerm_dns_zone.cncf.name_servers }" } +output "internal-zone-id" { value = "${ azurerm_dns_zone.cncf.zone_id }" } +output "name-servers-file" { value = "${ var.name-servers-file }" } diff --git a/modules/etcd/.#cloud-config.tf b/modules/etcd/.#cloud-config.tf new file mode 120000 index 0000000..47d290a --- /dev/null +++ b/modules/etcd/.#cloud-config.tf @@ -0,0 +1 @@ +dlx@W541.15790:1490082843 \ No newline at end of file diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf new file mode 100644 index 0000000..4dbb095 --- /dev/null +++ b/modules/etcd/cloud-config.tf @@ -0,0 +1,24 @@ +data "template_file" "cloud-config" { + count = "${ var.master-node-count }" + template = "${ file( "${ path.module }/cloud-config.yml" )}" + + vars { + # bucket = "${ var.bucket-prefix }" + cluster-domain = "${ var.cluster-domain }" + cluster-token = "etcd-cluster-${ var.name }" + dns-service-ip = "${ var.dns-service-ip }" + etc-tar = "/manifests/etc.tar" + fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" + hostname = "etcd${ count.index + 1 }" + kubelet-image-url = "${ var.kubelet-image-url }" + kubelet-image-tag = "${ var.kubelet-image-tag }" + internal-tld = "${ var.internal-tld }" + pod-cidr = "${ var.pod-cidr }" + location = "${ var.location }" + service-cidr = "${ var.service-cidr }" + k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" + node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" + cloud-config = "${ base64encode(var.cloud-config) }" + + } +} diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml new file mode 100644 index 0000000..3f67928 --- /dev/null +++ b/modules/etcd/cloud-config.yml @@ -0,0 +1,280 @@ +#cloud-config + +--- +coreos: + + etcd2: + advertise-client-urls: http://${ fqdn }:2379 + # cert-file: /etc/kubernetes/ssl/k8s-etcd.pem + debug: true + discovery-srv: ${ internal-tld } + initial-advertise-peer-urls: https://${ fqdn }:2380 + initial-cluster-state: new + initial-cluster-token: ${ cluster-token } + # key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem + listen-client-urls: http://${ node-ip }:2379,http://127.0.0.1:2379 + listen-peer-urls: https://${ node-ip }:2380 + name: ${ hostname } + peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem + peer-client-cert-auth: true + peer-cert-file: /etc/kubernetes/ssl/k8s-etcd.pem + peer-key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem + + units: + - name: etcd2.service + command: start + drop-ins: + - name: wait-for-certs.conf + content: | + [Unit] + After=get-ssl.service + Requires=get-ssl.service + + - name: docker.service + command: start + drop-ins: + - name: overlay.conf + content: | + [Service] + Environment="DOCKER_OPTS=--storage-driver=overlay" + + - name: get-ssl.service + command: start + content: | + [Service] + ExecStart=/bin/sh -c "tar xv -f /etc/kubernetes/ssl/k8s-apiserver.tar -C /etc/kubernetes/ssl/" + RemainAfterExit=yes + Type=oneshot + + - name: kubelet.service + command: start + content: | + [Unit] + ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper + [Service] + Environment="KUBELET_IMAGE_URL=${ kubelet-image-url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet-image-tag }" + Environment="RKT_OPTS=\ + --volume dns,kind=host,source=/etc/resolv.conf \ + --mount volume=dns,target=/etc/resolv.conf \ + --volume rkt,kind=host,source=/opt/bin/host-rkt \ + --mount volume=rkt,target=/usr/bin/rkt \ + --volume var-lib-rkt,kind=host,source=/var/lib/rkt \ + --mount volume=var-lib-rkt,target=/var/lib/rkt \ + --volume stage,kind=host,source=/tmp \ + --mount volume=stage,target=/tmp \ + --volume var-log,kind=host,source=/var/log \ + --mount volume=var-log,target=/var/log" + ExecStartPre=/usr/bin/mkdir -p /var/log/containers + ExecStartPre=/usr/bin/mkdir -p /var/lib/kubelet + ExecStartPre=/usr/bin/mount --bind /var/lib/kubelet /var/lib/kubelet + ExecStartPre=/usr/bin/mount --make-shared /var/lib/kubelet + ExecStart=/usr/lib/coreos/kubelet-wrapper \ + --allow-privileged=true \ + --api-servers=http://127.0.0.1:8080 \ + --cloud-provider=azure \ + --cloud-config=/etc/kubernetes/ssl/azure-config.json \ + --cluster-dns=${ dns-service-ip } \ + --cluster-domain=${ cluster-domain } \ + --config=/etc/kubernetes/manifests \ + --register-schedulable=false + Restart=always + RestartSec=5 + [Install] + WantedBy=multi-user.target + + update: + reboot-strategy: etcd-lock + +write-files: + - path: /opt/bin/host-rkt + permissions: 0755 + owner: root:root + content: | + #!/bin/sh + exec nsenter -m -u -i -n -p -t 1 -- /usr/bin/rkt "$@" + + - path: /etc/kubernetes/manifests/kube-apiserver.yml + content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-apiserver + namespace: kube-system + spec: + hostNetwork: true + containers: + - name: kube-apiserver + image: ${ kubelet-image-url }:${ kubelet-image-tag } + command: + - /hyperkube + - apiserver + - --admission-control=LimitRanger + - --admission-control=NamespaceExists + - --admission-control=NamespaceLifecycle + - --admission-control=ResourceQuota + - --admission-control=SecurityContextDeny + - --admission-control=ServiceAccount + - --allow-privileged=true + - --client-ca-file=/etc/kubernetes/ssl/ca.pem + - --cloud-provider=azure + - --cloud-config=/etc/kubernetes/ssl/azure-config.json + - --etcd-servers=http://etcd.${ internal-tld }:2379 + - --insecure-bind-address=0.0.0.0 + - --secure-port=443 + - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --service-cluster-ip-range=${ service-cidr } + - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem + - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --v=2 + livenessProbe: + httpGet: + host: 127.0.0.1 + port: 8080 + path: /healthz + initialDelaySeconds: 15 + timeoutSeconds: 15 + ports: + - containerPort: 443 + hostPort: 443 + name: https + - containerPort: 8080 + hostPort: 8080 + name: local + volumeMounts: + - mountPath: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + readOnly: true + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host + + - path: /etc/kubernetes/manifests/kube-controller-manager.yml + content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-controller-manager + namespace: kube-system + spec: + hostNetwork: true + containers: + - name: kube-controller-manager + image: ${ kubelet-image-url }:${ kubelet-image-tag } + command: + - /hyperkube + - controller-manager + - --cloud-provider=azure + - --cloud-config=/etc/kubernetes/ssl/azure-config.json + - --leader-elect=true + - --master=http://127.0.0.1:8080 + - --root-ca-file=/etc/kubernetes/ssl/ca.pem + - --service-account-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + resources: + requests: + cpu: 200m + livenessProbe: + httpGet: + host: 127.0.0.1 + path: /healthz + port: 10252 + initialDelaySeconds: 15 + timeoutSeconds: 1 + volumeMounts: + - mountPath: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + readOnly: true + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host + + - path: /etc/kubernetes/manifests/kube-proxy.yml + content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-proxy + namespace: kube-system + spec: + hostNetwork: true + containers: + - name: kube-proxy + image: ${ kubelet-image-url }:${ kubelet-image-tag } + command: + - /hyperkube + - proxy + - --master=http://127.0.0.1:8080 + - --proxy-mode=iptables + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host + + - path: /etc/kubernetes/manifests/kube-scheduler.yml + content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-scheduler + namespace: kube-system + spec: + hostNetwork: true + containers: + - name: kube-scheduler + image: ${ kubelet-image-url }:${ kubelet-image-tag } + command: + - /hyperkube + - scheduler + - --leader-elect=true + - --master=http://127.0.0.1:8080 + resources: + requests: + cpu: 100m + livenessProbe: + httpGet: + host: 127.0.0.1 + path: /healthz + port: 10251 + initialDelaySeconds: 15 + timeoutSeconds: 1 + + - path: /etc/logrotate.d/docker-containers + content: | + /var/lib/docker/containers/*/*.log { + rotate 7 + daily + compress + size=1M + missingok + delaycompress + copytruncate + } + + - encoding: b64 + content: ${ k8s-apiserver-tar } + path: /etc/kubernetes/ssl/k8s-apiserver.tar + + - encoding: b64 + content: ${ cloud-config } + path: /etc/kubernetes/ssl/azure-config.json + diff --git a/modules/etcd/input.tf b/modules/etcd/input.tf new file mode 100644 index 0000000..318efaf --- /dev/null +++ b/modules/etcd/input.tf @@ -0,0 +1,26 @@ +variable "location" {} +variable "subnet-id" {} +variable "name" {} +variable "master-vm-size" {} +variable "master-node-count" {} +variable "image-publisher" {} +variable "image-offer" {} +variable "image-sku" {} +variable "image-version" {} +variable "availability-id" {} +variable "storage-account" {} +variable "storage-primary-endpoint" {} +variable "storage-container" {} +variable "k8s-apiserver-tar" {} +variable "cluster-domain" {} +variable "dns-service-ip" {} +variable "internal-tld" {} +variable "pod-cidr" {} +variable "service-cidr" {} +variable "admin-username" {} +variable "kubelet-image-url" {} +variable "kubelet-image-tag" {} +variable "cloud-config" {} + +# variable "etcd-security-group-id" {} +# variable "external-elb-security-group-id" {} diff --git a/modules/etcd/load-balancer.tf b/modules/etcd/load-balancer.tf new file mode 100644 index 0000000..1266605 --- /dev/null +++ b/modules/etcd/load-balancer.tf @@ -0,0 +1,47 @@ +resource "azurerm_public_ip" "cncf" { + name = "PublicIPForLB" + location = "${ var.location }" + resource_group_name = "${ var.name }" + public_ip_address_allocation = "static" + domain_name_label = "k8s${ var.name }" +} + +resource "azurerm_lb" "cncf" { + name = "TestLoadBalancer" + location = "${ azurerm_public_ip.cncf.location }" + resource_group_name = "${ azurerm_public_ip.cncf.resource_group_name }" + + frontend_ip_configuration { + name = "PublicIPAddress" + public_ip_address_id = "${azurerm_public_ip.cncf.id}" + } +} + +resource "azurerm_lb_rule" "cncf" { + resource_group_name = "${azurerm_public_ip.cncf.resource_group_name}" + loadbalancer_id = "${azurerm_lb.cncf.id}" + probe_id = "${ azurerm_lb_probe.cncf.id }" + backend_address_pool_id = "${ azurerm_lb_backend_address_pool.cncf.id }" + name = "LBRule" + protocol = "Tcp" + frontend_port = 443 + backend_port = 443 + frontend_ip_configuration_name = "PublicIPAddress" +} + +resource "azurerm_lb_probe" "cncf" { + resource_group_name = "${azurerm_public_ip.cncf.resource_group_name}" + loadbalancer_id = "${azurerm_lb.cncf.id}" + name = "${ var.name }" + protocol = "Http" + port = 8080 + request_path = "/" + interval_in_seconds = 30 + number_of_probes = 5 +} + +resource "azurerm_lb_backend_address_pool" "cncf" { + resource_group_name = "${ azurerm_public_ip.cncf.resource_group_name }" + loadbalancer_id = "${azurerm_lb.cncf.id}" + name = "BackEndAddressPool" +} diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf new file mode 100644 index 0000000..b96a4fc --- /dev/null +++ b/modules/etcd/nodes.tf @@ -0,0 +1,53 @@ +resource "azurerm_network_interface" "cncf" { + count = "${ var.master-node-count }" + name = "etcd-interface${ count.index + 1 }" + location = "${ var.location }" + resource_group_name = "${ var.name }" + + ip_configuration { + name = "etcd-nic${ count.index + 1 }" + subnet_id = "${ var.subnet-id }" + private_ip_address_allocation = "dynamic" + # private_ip_address = "${ element( split(",", var.etcd-ips), count.index ) }" + load_balancer_backend_address_pools_ids = ["${ azurerm_lb_backend_address_pool.cncf.id }"] + } +} + +resource "azurerm_virtual_machine" "cncf" { + count = "${ var.master-node-count }" + name = "etcd-master${ count.index + 1 }" + location = "${ var.location }" + availability_set_id = "${ var.availability-id }" + resource_group_name = "${ var.name }" + network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] + vm_size = "${ var.master-vm-size }" + + storage_image_reference { + publisher = "${ var.image-publisher }" + offer = "${ var.image-offer }" + sku = "${ var.image-sku }" + version = "${ var.image-version}" + } + + storage_os_disk { + name = "etcd-disks${ count.index + 1 }" + vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/etcd-vhd${ count.index + 1 }.vhd" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "etcd-master${ count.index + 1 }" + admin_username = "${ var.admin-username }" + admin_password = "Password1234!" + custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" + } + + os_profile_linux_config { + disable_password_authentication = true + ssh_keys { + path = "/home/${ var.admin-username }/.ssh/authorized_keys" + key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" + } + } +} diff --git a/modules/etcd/output.tf b/modules/etcd/output.tf new file mode 100644 index 0000000..1e7929a --- /dev/null +++ b/modules/etcd/output.tf @@ -0,0 +1,8 @@ +output "external-lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } +output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } + + +# output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } +# output "external-elb" { value = "${ aws_elb.external.dns_name }" } +# output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } +output "master-ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } diff --git a/modules/kubeconfig/io.tf b/modules/kubeconfig/io.tf new file mode 100644 index 0000000..fda7916 --- /dev/null +++ b/modules/kubeconfig/io.tf @@ -0,0 +1,8 @@ +variable "admin-key-pem" {} +variable "admin-pem" {} +variable "ca-pem" {} +variable "fqdn-k8s" {} +variable "name" {} + + +output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/modules/kubeconfig/kubeconfig.tf b/modules/kubeconfig/kubeconfig.tf new file mode 100644 index 0000000..90c3a7b --- /dev/null +++ b/modules/kubeconfig/kubeconfig.tf @@ -0,0 +1,50 @@ +data "template_file" "kubeconfig" { + template = < ./tmp/kubeconfig +${data.template_file.kubeconfig.rendered} +__USERDATA__ +LOCAL_EXEC + } + + provisioner "local-exec" { + command = < "1" + # address_space.0: "" => "10.0.0.0/16" + # dns_servers.#: "" => "4" + # dns_servers.0: "" => "40.90.4.9" + # dns_servers.1: "" => "13.107.24.9" + # dns_servers.2: "" => "64.4.48.9" + # dns_servers.3: "" => "13.107.160.9" + + # tags { + #builtWith = "terraform" + #KubernetesCluster = "${ var.name }" + #z8s = "${ var.name }" + #Name = "kz8s-${ var.name }" + #version = "${ var.hyperkube-tag }" + #visibility = "private,public" + #} +} + +resource "azurerm_route_table" "cncf" { + name = "${ var.name }" + location = "${ var.location }" + resource_group_name = "${ var.name }" +} diff --git a/modules/worker/cloud-config.tf b/modules/worker/cloud-config.tf new file mode 100644 index 0000000..76fa63d --- /dev/null +++ b/modules/worker/cloud-config.tf @@ -0,0 +1,14 @@ +data "template_file" "cloud-config" { + template = "${ file( "${ path.module }/cloud-config.yml" )}" + + vars { + cluster-domain = "${ var.cluster-domain }" + dns-service-ip = "${ var.dns-service-ip }" + kubelet-image-url = "${ var.kubelet-image-url }" + kubelet-image-tag = "${ var.kubelet-image-tag }" + internal-tld = "${ var.internal-tld }" + location = "${ var.location }" + k8s-worker-tar = "${ base64encode(var.k8s-worker-tar) }" + cloud-config = "${ base64encode(var.cloud-config) }" + } +} diff --git a/modules/worker/cloud-config.yml b/modules/worker/cloud-config.yml new file mode 100644 index 0000000..36d1937 --- /dev/null +++ b/modules/worker/cloud-config.yml @@ -0,0 +1,158 @@ +#cloud-config + +--- +coreos: + + etcd2: + discovery-srv: ${ internal-tld } + peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem + peer-client-cert-auth: true + peer-cert-file: /etc/kubernetes/ssl/k8s-worker.pem + peer-key-file: /etc/kubernetes/ssl/k8s-worker-key.pem + proxy: on + + units: + - name: etcd2.service + command: start + + - name: docker.service + command: start + + - name: get-ssl.service + command: start + content: | + [Service] + ExecStart=/bin/sh -c "tar xv -f /etc/kubernetes/ssl/k8s-worker.tar -C /etc/kubernetes/ssl/" + RemainAfterExit=yes + Type=oneshot + + - name: kubelet.service + command: start + content: | + [Unit] + ConditionFileIsExecutable=/usr/lib/coreos/kubelet-wrapper + [Service] + Environment="KUBELET_IMAGE_URL=${ kubelet-image-url }" + Environment="KUBELET_IMAGE_TAG=${ kubelet-image-tag }" + Environment="RKT_OPTS=\ + --volume dns,kind=host,source=/etc/resolv.conf \ + --mount volume=dns,target=/etc/resolv.conf \ + --volume rkt,kind=host,source=/opt/bin/host-rkt \ + --mount volume=rkt,target=/usr/bin/rkt \ + --volume var-lib-rkt,kind=host,source=/var/lib/rkt \ + --mount volume=var-lib-rkt,target=/var/lib/rkt \ + --volume stage,kind=host,source=/tmp \ + --mount volume=stage,target=/tmp \ + --volume var-log,kind=host,source=/var/log \ + --mount volume=var-log,target=/var/log" + ExecStartPre=/usr/bin/mkdir -p /var/log/containers + ExecStartPre=/usr/bin/mkdir -p /var/lib/kubelet + ExecStartPre=/usr/bin/mount --bind /var/lib/kubelet /var/lib/kubelet + ExecStartPre=/usr/bin/mount --make-shared /var/lib/kubelet + ExecStart=/usr/lib/coreos/kubelet-wrapper \ + --allow-privileged=true \ + --api-servers=http://master.${ internal-tld }:8080 \ + --cloud-provider=azure \ + --cloud-config=/etc/kubernetes/ssl/azure-config.json \ + --cluster-dns=${ dns-service-ip } \ + --cluster-domain=${ cluster-domain } \ + --config=/etc/kubernetes/manifests \ + --kubeconfig=/etc/kubernetes/kubeconfig.yml \ + --register-node=true \ + --tls-cert-file=/etc/kubernetes/ssl/k8s-worker.pem \ + --tls-private-key-file=/etc/kubernetes/ssl/k8s-worker-key.pem + Restart=always + RestartSec=5 + [Install] + WantedBy=multi-user.target + + update: + reboot-strategy: etcd-lock + +write-files: + - path: /opt/bin/host-rkt + permissions: 0755 + owner: root:root + content: | + #!/bin/sh + exec nsenter -m -u -i -n -p -t 1 -- /usr/bin/rkt "$@" + + - path: /etc/kubernetes/kubeconfig.yml + content: | + apiVersion: v1 + kind: Config + clusters: + - name: local + cluster: + certificate-authority: /etc/kubernetes/ssl/ca.pem + users: + - name: kubelet + user: + client-certificate: /etc/kubernetes/ssl/k8s-worker.pem + client-key: /etc/kubernetes/ssl/k8s-worker-key.pem + contexts: + - context: + cluster: local + user: kubelet + name: kubelet-context + current-context: kubelet-context + + - path: /etc/kubernetes/manifests/kube-proxy.yml + content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-proxy + namespace: kube-system + spec: + hostNetwork: true + containers: + - name: kube-proxy + image: ${ kubelet-image-url }:${ kubelet-image-tag } + command: + - /hyperkube + - proxy + - --kubeconfig=/etc/kubernetes/kubeconfig.yml + - --master=https://master.${ internal-tld } + - --proxy-mode=iptables + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/ssl/certs + name: "ssl-certs" + - mountPath: /etc/kubernetes/kubeconfig.yml + name: "kubeconfig" + readOnly: true + - mountPath: /etc/kubernetes/ssl + name: "etc-kube-ssl" + readOnly: true + volumes: + - name: "ssl-certs" + hostPath: + path: "/usr/share/ca-certificates" + - name: "kubeconfig" + hostPath: + path: "/etc/kubernetes/kubeconfig.yml" + - name: "etc-kube-ssl" + hostPath: + path: "/etc/kubernetes/ssl" + + - path: /etc/logrotate.d/docker-containers + content: | + /var/lib/docker/containers/*/*.log { + rotate 7 + daily + compress + size=1M + missingok + delaycompress + copytruncate + } + + - encoding: b64 + content: ${ k8s-worker-tar } + path: /etc/kubernetes/ssl/k8s-worker.tar + + - encoding: b64 + content: ${ cloud-config } + path: /etc/kubernetes/ssl/azure-config.json diff --git a/modules/worker/input.tf b/modules/worker/input.tf new file mode 100644 index 0000000..e5d9d19 --- /dev/null +++ b/modules/worker/input.tf @@ -0,0 +1,22 @@ +variable "location" {} +variable "subnet-id" {} +variable "name" {} +variable "worker-vm-size" {} +variable "worker-node-count" {} +variable "image-publisher" {} +variable "image-offer" {} +variable "image-sku" {} +variable "image-version" {} +variable "storage-account" {} +variable "storage-primary-endpoint" {} +variable "storage-container" {} +variable "availability-id" {} +variable "external-lb" {} +variable "cluster-domain" {} +variable "dns-service-ip" {} +variable "internal-tld" {} +variable "admin-username" {} +variable "k8s-worker-tar" {} +variable "kubelet-image-url" {} +variable "kubelet-image-tag" {} +variable "cloud-config" {} diff --git a/modules/worker/nodes.tf b/modules/worker/nodes.tf new file mode 100644 index 0000000..42bde3f --- /dev/null +++ b/modules/worker/nodes.tf @@ -0,0 +1,105 @@ +resource "azurerm_network_interface" "cncf" { + count = "${ var.worker-node-count }" + name = "worker-interface${ count.index + 1 }" + location = "${ var.location }" + resource_group_name = "${ var.name }" + + ip_configuration { + name = "worker-nic${ count.index + 1 }" + subnet_id = "${ var.subnet-id }" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_virtual_machine" "cncf" { + count = "${ var.worker-node-count }" + name = "worker-node${ count.index + 1 }" + location = "${ var.location }" + availability_set_id = "${ var.availability-id }" + resource_group_name = "${ var.name }" + network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] + vm_size = "${ var.worker-vm-size }" + + storage_image_reference { + publisher = "${ var.image-publisher }" + offer = "${ var.image-offer }" + sku = "${ var.image-sku }" + version = "${ var.image-version}" + } + + storage_os_disk { + name = "worker-disks${ count.index + 1 }" + vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/worker-vhd${ count.index + 1 }.vhd" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "worker-node${ count.index + 1 }" + admin_username = "${ var.admin-username }" + admin_password = "Password1234!" + custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" + } + + os_profile_linux_config { + disable_password_authentication = true + ssh_keys { + path = "/home/${ var.admin-username }/.ssh/authorized_keys" + key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" + } + } +} + +/* +resource "azurerm_virtual_machine_scale_set" "cncf" { + name = "${ var.name }" + location = "${ var.location }" + resource_group_name = "${ var.name }" + upgrade_policy_mode = "Manual" + + sku { + name = "Standard_A0" + tier = "Standard" + capacity = 2 + } + + os_profile { + computer_name_prefix = "worker" + admin_username = "${ var.admin-username }" + admin_password = "Password1234" + custom_data = "${ data.template_file.cloud-config.rendered }" + } + + os_profile_linux_config { + disable_password_authentication = true + ssh_keys { + path = "/home/${ var.admin-username }/.ssh/authorized_keys" + key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" + } + } + + network_profile { + name = "TestNetworkProfile" + primary = true + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${ var.subnet-id }" + # load_balancer_backend_address_pool_ids = ["${ var.external-lb }"] + } + } + + storage_profile_os_disk { + name = "osDiskProfile" + caching = "ReadWrite" + create_option = "FromImage" + vhd_containers = ["${ var.storage-primary-endpoint }${ var.storage-container }"] + } + + storage_profile_image_reference { + publisher = "CoreOS" + offer = "CoreOS" + sku = "Stable" + version = "1298.6.0" + } +} +*/ diff --git a/output.tf b/output.tf new file mode 100644 index 0000000..0ea809d --- /dev/null +++ b/output.tf @@ -0,0 +1,22 @@ +output "fqdn-k8s" { value = "${ module.etcd.fqdn-lb}" } +output "bastion-ip" { value = "${ module.bastion.bastion-ip}" } +output "bastion-fqdn" { value = "${ module.bastion.bastion-fqdn}" } +output "k8s-admin" { value = "${ k8s-admin}"} +# fixme for use outside container +output "ssh-key-setup" { value = "eval $(ssh-agent) ; ssh-add /cncf/data/.ssh/id_rsa"} +output "ssh-via-bastion" { value = "ssh -At ${ var.admin-username }@${ module.bastion.bastion-fqdn } ssh ${ var.admin-username }@master1.cncf.demo"} + +#output "availability-id" { value = "${ azurerm_availability_set.cncf.id }" } +#output "azs" { value = "${ var.aws["azs"] }" } +#output "bastion-ip" { value = "${ module.bastion.ip }" } +#output "cluster-domain" { value = "${ var.cluster-domain }" } +#output "dns-service-ip" { value = "${ var.dns-service-ip }" } +#output "etcd1-ip" { value = "${ element( split(",", var.etcd-ips), 0 ) }" } +#output "external-elb" { value = "${ module.etcd.external-elb }" } +#output "internal-tld" { value = "${ var.internal-tld }" } +#output "name" { value = "${ var.name }" } +#output "region" { value = "${ var.aws["region"] }" } +#output "s3-bucket" { value = "${ var.s3-bucket }" } +#output "subnet-ids-private" { value = "${ module.vpc.subnet-ids-private }" } +#output "subnet-ids-public" { value = "${ module.vpc.subnet-ids-public }" } +#output "worker-autoscaling-group-name" { value = "${ module.worker.autoscaling-group-name }" } diff --git a/readme.org b/readme.org new file mode 100644 index 0000000..4cec32b --- /dev/null +++ b/readme.org @@ -0,0 +1,211 @@ +# -*- org-use-property-inheritance: t; -*- +#+TITLE: CNCF Demo on Azure +#+AUTHOR: Hippie Hacker +#+EMAIL: hh@ii.coop +#+CREATOR: ii.coop +#+DATE: March 1st, 2017 +#+PROPERTY: header-args :dir "." +#+NOTPROPERTY: header-args:shell :prologue ". .env_prod ; . ~/.rvm/scripts/rvm" +#+PROPERTY: header-args:shell :session none :exports both :cache yes +* tldr + +#+NAME: tldr +#+BEGIN_SRC shell +docker run -v $(pwd)/data:/data -ti generate/creds:azure +# this will ask you to launch a web browser to authenticate to azure +# it will result in ./data/azure.env +# deployname can only consist of lowercase letters and numbers, and must be less than 18 characters long +DEPLOYNAME=$(date +%Y%m%d%H%M) # could be a branch+gitcommit etc +# all data for the deploy will be stored in it's own directory +docker run -ti -v $(pwd)/data/$DEPLOYNAME:/cncf/data --env-file ./data/azure.env create/azure deploy $DEPLOYNAME +#+END_SRC + +* access your k8s cluster + +#+NAME: 3 minute deploy +#+BEGIN_SRC output +bastion-fqdn = bastion201703230324.westus.cloudapp.azure.com +fqdn-k8s = k8s201703230324.westus.cloudapp.azure.com +ssh-key-setup = eval $(ssh-agent) ; ssh-add /cncf/data/.ssh/id_rsa +ssh-via-bastion = ssh -At cncf@bastion201703230324.westus.cloudapp.azure.com ssh cncf@master1.cncf.demo + +real 3m2.509s +user 0m4.460s +sys 0m0.668s +# takes about 3-8 minutes to deploy + +sudo chown -R $(whoami):$(whoami) $(pwd)/data/${DEPLOYNAME} +export KUBECONFIG=$(pwd)/data/${DEPLOYNAME}/kubeconfig + +kubectl get nodes +NAME STATUS AGE +etcd-master1 Ready,SchedulingDisabled 13m +etcd-master2 Ready,SchedulingDisabled 13m +etcd-master3 Ready,SchedulingDisabled 14m +worker-node1 Ready 10m +worker-node2 Ready 13m +worker-node3 Ready 13m +... +kubectl proxy etc + +# to destroy +docker run -ti -v $(pwd)/data/$DEPLOYNAME:/cncf/data --env-file ./data/azure.env terminate/azure destroy $DEPLOYNAME +#+END_SRC + +Using DEPLOYNAME allows for multiple concurrent deploys and to easily. + +* Customing the Deploy via environment + +Adding var-name=value to ./data/terraform.tfvars will allow you to override many settings for this deploy. + +#+BEGIN_SRC shell +name= "azure" +location= "westus" +internal-tld= "cncf.demo" +master-node-count= "3" +worker-node-count= "3" +master-vm-size= "Standard_A2" +worker-vm-size= "Standard_A2" +bastion-vm-size= "Standard_A2" +kubelet-image-url= "quay.io/coreos/hyperkube" +kubelet-image-tag= "v1.4.7_coreos.0" +image-publisher= "CoreOS" +image-offer= "CoreOS" +image-sku= "Stable" +image-version= "1298.6.0" +#+END_SRC + +* Next steps + +- Store private keys in storage rather than cloud-init +- Look into using Centos and Debian/Ubuntu +- Deploy onto AWS and Azure at the same time +- Setup some CI to deploy on every commit to both clouds +- Start on GCE + +* Notable issues + +** Azure dns_zones do not provide IPs +Specifically [[https://www.terraform.io/docs/providers/azurerm/r/dns_zone.html#name_servers][azurerm_dns_zone name_servers]] only provides the server names, while [[https://www.terraform.io/docs/providers/azurerm/r/network_interface.html#dns_servers][azurerm_network_interface requires a list of IPs]]. +We'll do a cleaner maping later, but the [[https://github.com/cncf/demo/pull/194/files#diff-8f1d08cae7f5b62ea7e23f2cb3b0b67bR7][current hack]] got us IPs and +** Azure CNAME records don't resolve correctly +We had a couple places where CNAMEs behaved unexpectedly when using Azure dns zones. +Specifically CNAME records when queried with DIG wouldn't refer. +** Terraform azurerm_dns_srv_records do not support multiple dynamic entries +This affects our ability to bootstrap etcd with an unknown number of nodes beforehand. +If we stick with three (or any number) it's not a problem. +** Azure Cluster-Autoscale Virtual Machine Scale Sets are not yet supported by kubernetes +We should be able to scale up our workers at some point [[https://github.com/Azure/ACS/blob/master/kubernetes-status.md#future-work][in the future]]. +** Starting kubelet without --cloud-config=azure.json results in a panic +When using --cloud-provider=azure not only must you use +--cloud-config=azure.json, it seems you have to provide all the optional +settings as well. Failure to do so results in a panic. +** Hostnames and VM names must match in order for kubelet to find instances +[[https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/kubelet_node_status.go#L255][instances are looked up via nodeName]] and if they don't match, kubelet will not start. + +#+BEGIN_SRC example +kubelet_node_status.go:69] Unable to construct api.Node object for kubelet: + failed to get external ID from cloud provider: instance not found +#+END_SRC + +* generate credentials +** via a container + +#+NAME: generate/creds:azure +#+BEGIN_SRC shell +$ docker run -v $(pwd)/data:/data -ti generate/creds:azure +To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GY7W7BMRZ to authenticate. +Name CloudName SubscriptionId State IsDefault +------------- ----------- ------------------------------------ -------- ----------- +Free Trial AzureCloud 5358e673-95e7-4cd8-9791-ca28dd5e3cbb Disabled True +Pay-As-You-Go AzureCloud 70693672-7c0d-485f-ac08-06d458c80f0e Enabled + +Please enter the Name of the account you wish to use. If you do not see +a valid account in the list press Ctrl+C to abort and create one. +If you leave this blank we will use the Current account. +> Pay-As-You-Go +Using subscription_id: 70693672-7c0d-485f-ac08-06d458c80f0e +Using tenant_id: 9996322a-93ac-43ae-80be-887a3e8194a1 +==> Creating service principal +Retrying role assignment creation: 1/36 +Retrying role assignment creation: 2/36 +./data/azure.env created +$ cat ./data/azure.env +export ARM_SUBSCRIPTION_ID=70693672-XXXX-4858-ac08-06888888880e +export ARM_TENANT_ID=9896828a-93ac-43ae-YYYY-887a3e8898a1 +export ARM_CLIENT_ID=968448ae-f9f9-ZZZZ-bf43-5c081da88975 +export ARM_CLIENT_SECRET=BBBBBBBB-8eaa-AAAA-aafe-75b02ad4ceba +#+END_SRC + +** manually + +#+NAME: run az via docker +#+BEGIN_SRC +docker run -v $(pwd)/.azure:/root/.azure azuresdk/azure-cli-python az account list -o table +#+END_SRC + +#+NAME: az account list +#+BEGIN_SRC shell +az account list -o table +az account set --subscription Pay-As-You-Go +#+END_SRC + +#+RESULTS[eb0d69eb1ea1b9a005604b3dd37889127d19f76b]: az account list +| Name | CloudName | SubscriptionId | State | IsDefault | +| ------------- | ----------- | ------------------------------------ | -------- | ----------- | +| Free | Trial | AzureCloud | 5358e673-95e7-4cd8-9791-ca28dd5e3cbb | Disabled | +| Pay-As-You-Go | AzureCloud | 70693672-7c0d-485f-ac08-06d458c80f0e | Enabled | True | + +#+NAME: az account show table +#+BEGIN_SRC shell :results output verbatim raw +az account show -o table +#+END_SRC + +#+RESULTS[00afff595364da643372e54234a45a775c1539ef]: az account show table +| EnvironmentName | IsDefault | Name | State | TenantId | +| ----------------- | ----------- | ------------- | ------- | ------------------------------------ | +| AzureCloud | True | Pay-As-You-Go | Enabled | 9996322a-93ac-43ae-80be-887a3e8194a1 | + +#+NAME: az_account_show_json +#+HEADERS: :wrap SRC js +#+HEADERS: :results output +#+BEGIN_SRC shell :export both +az account show +#+END_SRC + +#+RESULTS[97a6b7ba7839519d9223a4e67e27ced7ed78f0b9]: az_account_show_json +#+BEGIN_SRC js +{ + "environmentName": "AzureCloud", + "id": "70693672-7c0d-485f-ac08-06d458c80f0e", + "isDefault": true, + "name": "Pay-As-You-Go", + "state": "Enabled", + "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", + "user": { + "name": "azure@ii.coop", + "type": "user" + } +} +#+END_SRC + +#+NAME: generate ENV +#+BEGIN_SRC shell +ARM_SUBSCRIPTION_ID=$( az account show | jq -r .id ) +CREDS_JSON=$( az ad sp create-for-rbac --name cncfdemos ) +ARM_TENANT_ID=$( echo ${CREDS_JSON} | jq -r .tenant ) +ARM_CLIENT_ID=$( echo ${CREDS_JSON} | jq -r .appId ) +ARM_CLIENT_SECRET=$( echo ${CREDS_JSON} | jq -r .password ) +echo export ARM_SUBSCRIPTION_ID=$ARM_SUBSCRIPTION_ID +echo export ARM_TENANT_ID=$ARM_TENANT_ID +echo export ARM_CLIENT_ID=$ARM_CLIENT_ID +echo export ARM_CLIENT_SECRET=$ARM_CLIENT_SECRET +#+END_SRC + +# Local Variables: +# eval: (require (quote ob-shell)) +# eval: (require (quote ob-lisp)) +# eval: (require (quote ob-js)) +# eval: (org-babel-do-load-languages 'org-babel-load-languages '((js . t) (shell . t))) +# eval: (setenv "PATH" (concat (concat (getenv "HOME") "/bin:") (getenv "PATH") )) +# End: diff --git a/runme b/runme new file mode 100755 index 0000000..23d6360 --- /dev/null +++ b/runme @@ -0,0 +1,12 @@ +rm -rf /cncf/data/.ssh/ +rm -rf /cncf/data/.cfssl/ +rm -rf /cncf/data/azure-config.json +rm -rf /build/azure/terraform.tfstate* +rm -rf /build/azure/azure_dns* +terraform get +terraform apply -target null_resource.sshkey_gen +terraform apply -target null_resource.ssl_gen +terraform apply -target null_resource.cloud_gen +terraform apply -target module.dns.null_resource.dns_gen +terraform apply -target module.etcd.azurerm_network_interface.cncf +time terraform apply \ No newline at end of file diff --git a/wait-for-cluster b/wait-for-cluster new file mode 100755 index 0000000..be16fd3 --- /dev/null +++ b/wait-for-cluster @@ -0,0 +1,18 @@ +#!/bin/bash -eu + +ELB=$(terraform output fqdn-k8s) + +_retry() { + [ -z "${2}" ] && return 1 + echo -n ${1} + until printf "." && "${@:2}" &>/dev/null; do sleep 5.2; done; echo "✓" +} + +echo "❤ Polling for cluster life - this could take a minute or more" + +_retry "❤ Waiting for DNS to resolve for ${ELB}" getent hosts "${ELB}" +_retry "❤ Curling apiserver external elb" curl --insecure --silent "https://${ELB}" +_retry "❤ Trying to connect to cluster with kubectl" kubectl cluster-info + +kubectl cluster-info +sleep 2 # FIXME: Maybe API was up, but scheduling wasn't quite up? From 53aead105b9209019f21b6512e2d3c9398fe3622 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 6 Apr 2017 09:59:03 +1200 Subject: [PATCH 069/149] Refactor Azure to GCE and Create VPC Resource --- azure.tf | 32 ------ cert.tf | 2 +- cloud-config.tf | 64 ++++++------ gce.tf | 36 +++++++ input.tf | 6 +- modules.tf | 192 +++++++++++++++++----------------- modules/vpc/azure-security.tf | 10 +- modules/vpc/azure-subnet.tf | 12 +-- modules/vpc/io.tf | 8 +- modules/vpc/vpc.tf | 43 +------- output.tf | 14 +-- 11 files changed, 192 insertions(+), 227 deletions(-) delete mode 100644 azure.tf create mode 100644 gce.tf diff --git a/azure.tf b/azure.tf deleted file mode 100644 index 2623b2d..0000000 --- a/azure.tf +++ /dev/null @@ -1,32 +0,0 @@ -# Configure the Microsoft Azure Provider -provider "azurerm" { } - -resource "azurerm_resource_group" "cncf" { - name = "${ var.name }" - location = "${ var.location }" -} - -resource "azurerm_storage_account" "cncf" { - # * azurerm_storage_account.cncf: name can only consist of lowercase letters - # and numbers, and must be between 3 and 24 characters long FIXME: - # storage_account name must be globally unique - name = "${ var.name }cncfdemo" - resource_group_name = "${ var.name }" - location = "${ var.location }" - account_type = "Standard_LRS" -} - -resource "azurerm_storage_container" "cncf" { - name = "${ var.name }" - resource_group_name = "${ var.name }" - storage_account_name = "${ azurerm_storage_account.cncf.name }" - container_access_type = "private" -} - -resource "azurerm_availability_set" "cncf" { - name = "${ var.name }" - resource_group_name = "${ var.name }" - location = "${ var.location }" - -} - diff --git a/cert.tf b/cert.tf index 49eeb10..6271c2f 100644 --- a/cert.tf +++ b/cert.tf @@ -5,7 +5,7 @@ resource "null_resource" "ssl_gen" { command = < ${ var.data-dir }/azure-config.json -{ - "aadClientId": "$${ARM_CLIENT_ID}", - "aadClientSecret": "$${ARM_CLIENT_SECRET}", - "tenantId": "$${ARM_TENANT_ID}", - "subscriptionId": "$${ARM_SUBSCRIPTION_ID}", - "resourceGroup": "${ var.name }", - "location": "${ var.location }", - "subnetName": "${ var.name }", - "securityGroupName": "${ var.name }", - "vnetName": "${ var.name }", - "routeTableName": "${ var.name }", - "primaryAvailabilitySetName": "${ var.name }" -} -JSON -COMMAND - } +# provisioner "local-exec" { +# command = < ${ var.data-dir }/azure-config.json +# { +# "aadClientId": "$${ARM_CLIENT_ID}", +# "aadClientSecret": "$${ARM_CLIENT_SECRET}", +# "tenantId": "$${ARM_TENANT_ID}", +# "subscriptionId": "$${ARM_SUBSCRIPTION_ID}", +# "resourceGroup": "${ var.name }", +# "location": "${ var.location }", +# "subnetName": "${ var.name }", +# "securityGroupName": "${ var.name }", +# "vnetName": "${ var.name }", +# "routeTableName": "${ var.name }", +# "primaryAvailabilitySetName": "${ var.name }" +# } +# JSON +# COMMAND +# } - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = < "1" - # address_space.0: "" => "10.0.0.0/16" - # dns_servers.#: "" => "4" - # dns_servers.0: "" => "40.90.4.9" - # dns_servers.1: "" => "13.107.24.9" - # dns_servers.2: "" => "64.4.48.9" - # dns_servers.3: "" => "13.107.160.9" - - # tags { - #builtWith = "terraform" - #KubernetesCluster = "${ var.name }" - #z8s = "${ var.name }" - #Name = "kz8s-${ var.name }" - #version = "${ var.hyperkube-tag }" - #visibility = "private,public" - #} + auto_create_subnetworks = "false" } -resource "azurerm_route_table" "cncf" { - name = "${ var.name }" - location = "${ var.location }" - resource_group_name = "${ var.name }" -} + diff --git a/output.tf b/output.tf index 0ea809d..f214cf2 100644 --- a/output.tf +++ b/output.tf @@ -1,10 +1,10 @@ -output "fqdn-k8s" { value = "${ module.etcd.fqdn-lb}" } -output "bastion-ip" { value = "${ module.bastion.bastion-ip}" } -output "bastion-fqdn" { value = "${ module.bastion.bastion-fqdn}" } -output "k8s-admin" { value = "${ k8s-admin}"} -# fixme for use outside container -output "ssh-key-setup" { value = "eval $(ssh-agent) ; ssh-add /cncf/data/.ssh/id_rsa"} -output "ssh-via-bastion" { value = "ssh -At ${ var.admin-username }@${ module.bastion.bastion-fqdn } ssh ${ var.admin-username }@master1.cncf.demo"} +# output "fqdn-k8s" { value = "${ module.etcd.fqdn-lb}" } +# output "bastion-ip" { value = "${ module.bastion.bastion-ip}" } +# output "bastion-fqdn" { value = "${ module.bastion.bastion-fqdn}" } +# output "k8s-admin" { value = "${ k8s-admin}"} +# # fixme for use outside container +# output "ssh-key-setup" { value = "eval $(ssh-agent) ; ssh-add /cncf/data/.ssh/id_rsa"} +# output "ssh-via-bastion" { value = "ssh -At ${ var.admin-username }@${ module.bastion.bastion-fqdn } ssh ${ var.admin-username }@master1.cncf.demo"} #output "availability-id" { value = "${ azurerm_availability_set.cncf.id }" } #output "azs" { value = "${ var.aws["azs"] }" } From 3630905cc68a5fef2edd2bf7fb309060fba8bf4d Mon Sep 17 00:00:00 2001 From: DLX Date: Fri, 7 Apr 2017 11:03:37 +1200 Subject: [PATCH 070/149] Refactor Vars and VPC-Subnet for GCE --- gce.tf | 15 ++++++++++++++- input.tf | 3 ++- modules/vpc/{azure-subnet.tf => gce-subnet.tf} | 0 3 files changed, 16 insertions(+), 2 deletions(-) rename modules/vpc/{azure-subnet.tf => gce-subnet.tf} (100%) diff --git a/gce.tf b/gce.tf index 8f97ff9..c4e53a9 100644 --- a/gce.tf +++ b/gce.tf @@ -1,10 +1,23 @@ # Configure the Microsoft Azure Provider provider "google" { credentials = "${file("gce.json")}" - project = "${ var.name }" + project = "${ var.project }" region = "${ var.region }" } +# resource "google_project" "company-env" { +# project_id = "${var.ENVIRONMENT}" +# org_id = "${var.GCP_ORG_ID}" +# name = "${var.ENVIRONMENT}" +# skip_delete = "true" +# } + +# // APIs to enable for above project +# resource "google_project_services" "company-env" { +# project = "${var.ENVIRONMENT}" +# services = ["compute_component", "container", "dns.googleapis.com", "sqladmin-json.googleapis.com", "monitoring.googleapis.com", "logging.googleapis.com", "sql-component-json.googleapis.com", "cloudmonitoring.googleapis.com", "storage-component-json.googleapis.com", "iam.googleapis.com"] +# } + # resource "azurerm_resource_group" "cncf" { # name = "${ var.name }" # location = "${ var.location }" diff --git a/input.tf b/input.tf index 89b1767..bf61cd4 100644 --- a/input.tf +++ b/input.tf @@ -1,4 +1,4 @@ -variable "name" { default = "custom-zone-163720" } +variable "name" { default = "test" } variable "internal-tld" { default = "cncf.demo" } variable "master-node-count" { default = "3" } variable "worker-node-count" { default = "3" } @@ -13,6 +13,7 @@ variable "image-offer" { default = "CoreOS" } variable "image-sku" { default = "Stable" } variable "image-version" { default = "1298.6.0" } variable "region" { default = "us-central1" } +variable "project" { default = "temporal-tensor-163817" } variable "cluster-domain" { default = "cluster.local" } variable "admin-username" { default = "cncf"} variable "cidr" { default = "10.0.0.0/16" } diff --git a/modules/vpc/azure-subnet.tf b/modules/vpc/gce-subnet.tf similarity index 100% rename from modules/vpc/azure-subnet.tf rename to modules/vpc/gce-subnet.tf From 6dabf97cb3cbb15965f6d9ab824d1865278206eb Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 05:19:10 +1200 Subject: [PATCH 071/149] Add etcd Host and Create 3 Nodes based on Count --- input.tf | 3 +- modules.tf | 16 +++-- modules/etcd/.#cloud-config.tf | 1 - modules/etcd/cloud-config.tf | 44 ++++++------ modules/etcd/input.tf | 45 ++++++------ modules/etcd/load-balancer.tf | 84 +++++++++++----------- modules/etcd/nodes.tf | 123 +++++++++++++++++++++------------ modules/etcd/output.tf | 12 ++-- 8 files changed, 185 insertions(+), 143 deletions(-) delete mode 120000 modules/etcd/.#cloud-config.tf diff --git a/input.tf b/input.tf index bf61cd4..e8bda74 100644 --- a/input.tf +++ b/input.tf @@ -13,7 +13,8 @@ variable "image-offer" { default = "CoreOS" } variable "image-sku" { default = "Stable" } variable "image-version" { default = "1298.6.0" } variable "region" { default = "us-central1" } -variable "project" { default = "temporal-tensor-163817" } +variable "zone" { default = "us-central1-a" } +variable "project" { default = "test-163823" } variable "cluster-domain" { default = "cluster.local" } variable "admin-username" { default = "cncf"} variable "cidr" { default = "10.0.0.0/16" } diff --git a/modules.tf b/modules.tf index cfba973..a2d6310 100644 --- a/modules.tf +++ b/modules.tf @@ -13,12 +13,14 @@ module "vpc" { # master-ips = "${ module.etcd.master-ips }" # } -# module "etcd" { -# source = "./modules/etcd" -# name = "${ var.name }" -# location = "${ var.location }" -# admin-username = "${ var.admin-username }" -# master-node-count = "${ var.master-node-count }" + module "etcd" { + source = "./modules/etcd" + name = "${ var.name }" + region = "${ var.region }" + zone = "${ var.zone }" + project = "${ var.project }" +# admin-username = "${ var.admin-username }" + master-node-count = "${ var.master-node-count }" # master-vm-size = "${ var.master-vm-size }" # image-publisher = "${ var.image-publisher }" # image-offer = "${ var.image-offer }" @@ -40,7 +42,7 @@ module "vpc" { # cloud-config = "${file("${ var.data-dir }/azure-config.json")}" # # etcd-security-group-id = "${ module.security.etcd-id }" # # external-elb-security-group-id = "${ module.security.external-elb-id }" -# } +} # module "bastion" { diff --git a/modules/etcd/.#cloud-config.tf b/modules/etcd/.#cloud-config.tf deleted file mode 120000 index 47d290a..0000000 --- a/modules/etcd/.#cloud-config.tf +++ /dev/null @@ -1 +0,0 @@ -dlx@W541.15790:1490082843 \ No newline at end of file diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index 4dbb095..c78399c 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -1,24 +1,24 @@ -data "template_file" "cloud-config" { - count = "${ var.master-node-count }" - template = "${ file( "${ path.module }/cloud-config.yml" )}" +# data "template_file" "cloud-config" { +# count = "${ var.master-node-count }" +# template = "${ file( "${ path.module }/cloud-config.yml" )}" - vars { - # bucket = "${ var.bucket-prefix }" - cluster-domain = "${ var.cluster-domain }" - cluster-token = "etcd-cluster-${ var.name }" - dns-service-ip = "${ var.dns-service-ip }" - etc-tar = "/manifests/etc.tar" - fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" - hostname = "etcd${ count.index + 1 }" - kubelet-image-url = "${ var.kubelet-image-url }" - kubelet-image-tag = "${ var.kubelet-image-tag }" - internal-tld = "${ var.internal-tld }" - pod-cidr = "${ var.pod-cidr }" - location = "${ var.location }" - service-cidr = "${ var.service-cidr }" - k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" - node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" - cloud-config = "${ base64encode(var.cloud-config) }" +# vars { +# # bucket = "${ var.bucket-prefix }" +# cluster-domain = "${ var.cluster-domain }" +# cluster-token = "etcd-cluster-${ var.name }" +# dns-service-ip = "${ var.dns-service-ip }" +# etc-tar = "/manifests/etc.tar" +# fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" +# hostname = "etcd${ count.index + 1 }" +# kubelet-image-url = "${ var.kubelet-image-url }" +# kubelet-image-tag = "${ var.kubelet-image-tag }" +# internal-tld = "${ var.internal-tld }" +# pod-cidr = "${ var.pod-cidr }" +# location = "${ var.location }" +# service-cidr = "${ var.service-cidr }" +# k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" +# node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" +# cloud-config = "${ base64encode(var.cloud-config) }" - } -} +# } +# } diff --git a/modules/etcd/input.tf b/modules/etcd/input.tf index 318efaf..35b36ad 100644 --- a/modules/etcd/input.tf +++ b/modules/etcd/input.tf @@ -1,26 +1,29 @@ -variable "location" {} -variable "subnet-id" {} +# variable "location" {} +# variable "subnet-id" {} variable "name" {} -variable "master-vm-size" {} +variable "region" {} +variable "zone" {} +variable "project" {} +# variable "master-vm-size" {} variable "master-node-count" {} -variable "image-publisher" {} -variable "image-offer" {} -variable "image-sku" {} -variable "image-version" {} -variable "availability-id" {} -variable "storage-account" {} -variable "storage-primary-endpoint" {} -variable "storage-container" {} -variable "k8s-apiserver-tar" {} -variable "cluster-domain" {} -variable "dns-service-ip" {} -variable "internal-tld" {} -variable "pod-cidr" {} -variable "service-cidr" {} -variable "admin-username" {} -variable "kubelet-image-url" {} -variable "kubelet-image-tag" {} -variable "cloud-config" {} +# variable "image-publisher" {} +# variable "image-offer" {} +# variable "image-sku" {} +# variable "image-version" {} +# variable "availability-id" {} +# variable "storage-account" {} +# variable "storage-primary-endpoint" {} +# variable "storage-container" {} +# variable "k8s-apiserver-tar" {} +# variable "cluster-domain" {} +# variable "dns-service-ip" {} +# variable "internal-tld" {} +# variable "pod-cidr" {} +# variable "service-cidr" {} +# variable "admin-username" {} +# variable "kubelet-image-url" {} +# variable "kubelet-image-tag" {} +# variable "cloud-config" {} # variable "etcd-security-group-id" {} # variable "external-elb-security-group-id" {} diff --git a/modules/etcd/load-balancer.tf b/modules/etcd/load-balancer.tf index 1266605..6d2bba3 100644 --- a/modules/etcd/load-balancer.tf +++ b/modules/etcd/load-balancer.tf @@ -1,47 +1,47 @@ -resource "azurerm_public_ip" "cncf" { - name = "PublicIPForLB" - location = "${ var.location }" - resource_group_name = "${ var.name }" - public_ip_address_allocation = "static" - domain_name_label = "k8s${ var.name }" -} +# resource "azurerm_public_ip" "cncf" { +# name = "PublicIPForLB" +# location = "${ var.location }" +# resource_group_name = "${ var.name }" +# public_ip_address_allocation = "static" +# domain_name_label = "k8s${ var.name }" +# } -resource "azurerm_lb" "cncf" { - name = "TestLoadBalancer" - location = "${ azurerm_public_ip.cncf.location }" - resource_group_name = "${ azurerm_public_ip.cncf.resource_group_name }" +# resource "azurerm_lb" "cncf" { +# name = "TestLoadBalancer" +# location = "${ azurerm_public_ip.cncf.location }" +# resource_group_name = "${ azurerm_public_ip.cncf.resource_group_name }" - frontend_ip_configuration { - name = "PublicIPAddress" - public_ip_address_id = "${azurerm_public_ip.cncf.id}" - } -} +# frontend_ip_configuration { +# name = "PublicIPAddress" +# public_ip_address_id = "${azurerm_public_ip.cncf.id}" +# } +# } -resource "azurerm_lb_rule" "cncf" { - resource_group_name = "${azurerm_public_ip.cncf.resource_group_name}" - loadbalancer_id = "${azurerm_lb.cncf.id}" - probe_id = "${ azurerm_lb_probe.cncf.id }" - backend_address_pool_id = "${ azurerm_lb_backend_address_pool.cncf.id }" - name = "LBRule" - protocol = "Tcp" - frontend_port = 443 - backend_port = 443 - frontend_ip_configuration_name = "PublicIPAddress" -} +# resource "azurerm_lb_rule" "cncf" { +# resource_group_name = "${azurerm_public_ip.cncf.resource_group_name}" +# loadbalancer_id = "${azurerm_lb.cncf.id}" +# probe_id = "${ azurerm_lb_probe.cncf.id }" +# backend_address_pool_id = "${ azurerm_lb_backend_address_pool.cncf.id }" +# name = "LBRule" +# protocol = "Tcp" +# frontend_port = 443 +# backend_port = 443 +# frontend_ip_configuration_name = "PublicIPAddress" +# } -resource "azurerm_lb_probe" "cncf" { - resource_group_name = "${azurerm_public_ip.cncf.resource_group_name}" - loadbalancer_id = "${azurerm_lb.cncf.id}" - name = "${ var.name }" - protocol = "Http" - port = 8080 - request_path = "/" - interval_in_seconds = 30 - number_of_probes = 5 -} +# resource "azurerm_lb_probe" "cncf" { +# resource_group_name = "${azurerm_public_ip.cncf.resource_group_name}" +# loadbalancer_id = "${azurerm_lb.cncf.id}" +# name = "${ var.name }" +# protocol = "Http" +# port = 8080 +# request_path = "/" +# interval_in_seconds = 30 +# number_of_probes = 5 +# } -resource "azurerm_lb_backend_address_pool" "cncf" { - resource_group_name = "${ azurerm_public_ip.cncf.resource_group_name }" - loadbalancer_id = "${azurerm_lb.cncf.id}" - name = "BackEndAddressPool" -} +# resource "azurerm_lb_backend_address_pool" "cncf" { +# resource_group_name = "${ azurerm_public_ip.cncf.resource_group_name }" +# loadbalancer_id = "${azurerm_lb.cncf.id}" +# name = "BackEndAddressPool" +# } diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index b96a4fc..e0d2fb4 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -1,53 +1,90 @@ -resource "azurerm_network_interface" "cncf" { - count = "${ var.master-node-count }" - name = "etcd-interface${ count.index + 1 }" - location = "${ var.location }" - resource_group_name = "${ var.name }" - - ip_configuration { - name = "etcd-nic${ count.index + 1 }" - subnet_id = "${ var.subnet-id }" - private_ip_address_allocation = "dynamic" - # private_ip_address = "${ element( split(",", var.etcd-ips), count.index ) }" - load_balancer_backend_address_pools_ids = ["${ azurerm_lb_backend_address_pool.cncf.id }"] +resource "google_compute_instance" "cncf" { + count = "${ var.master-node-count }" + name = "${ var.name }${ count.index + 1 }" + machine_type = "n1-standard-1" + zone = "${ var.zone }" + + tags = ["foo", "bar"] + + disk { + image = "debian-cloud/debian-8" } -} -resource "azurerm_virtual_machine" "cncf" { - count = "${ var.master-node-count }" - name = "etcd-master${ count.index + 1 }" - location = "${ var.location }" - availability_set_id = "${ var.availability-id }" - resource_group_name = "${ var.name }" - network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] - vm_size = "${ var.master-vm-size }" - - storage_image_reference { - publisher = "${ var.image-publisher }" - offer = "${ var.image-offer }" - sku = "${ var.image-sku }" - version = "${ var.image-version}" + // Local SSD disk + disk { + type = "local-ssd" + scratch = true } - storage_os_disk { - name = "etcd-disks${ count.index + 1 }" - vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/etcd-vhd${ count.index + 1 }.vhd" - caching = "ReadWrite" - create_option = "FromImage" + network_interface { + # network = "${ var.name }" + subnetwork = "${ var.name }" + subnetwork_project = "${ var.project }" + + access_config { + // Ephemeral IP + } } - os_profile { - computer_name = "etcd-master${ count.index + 1 }" - admin_username = "${ var.admin-username }" - admin_password = "Password1234!" - custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" + metadata { + foo = "bar" } - os_profile_linux_config { - disable_password_authentication = true - ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" - key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] } - } } + +# resource "azurerm_network_interface" "cncf" { +# count = "${ var.master-node-count }" +# name = "etcd-interface${ count.index + 1 }" +# location = "${ var.location }" +# resource_group_name = "${ var.name }" + +# ip_configuration { +# name = "etcd-nic${ count.index + 1 }" +# subnet_id = "${ var.subnet-id }" +# private_ip_address_allocation = "dynamic" +# # private_ip_address = "${ element( split(",", var.etcd-ips), count.index ) }" +# load_balancer_backend_address_pools_ids = ["${ azurerm_lb_backend_address_pool.cncf.id }"] +# } +# } + +# resource "azurerm_virtual_machine" "cncf" { +# count = "${ var.master-node-count }" +# name = "etcd-master${ count.index + 1 }" +# location = "${ var.location }" +# availability_set_id = "${ var.availability-id }" +# resource_group_name = "${ var.name }" +# network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] +# vm_size = "${ var.master-vm-size }" + +# storage_image_reference { +# publisher = "${ var.image-publisher }" +# offer = "${ var.image-offer }" +# sku = "${ var.image-sku }" +# version = "${ var.image-version}" +# } + +# storage_os_disk { +# name = "etcd-disks${ count.index + 1 }" +# vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/etcd-vhd${ count.index + 1 }.vhd" +# caching = "ReadWrite" +# create_option = "FromImage" +# } + +# os_profile { +# computer_name = "etcd-master${ count.index + 1 }" +# admin_username = "${ var.admin-username }" +# admin_password = "Password1234!" +# custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" +# } + +# os_profile_linux_config { +# disable_password_authentication = true +# ssh_keys { +# path = "/home/${ var.admin-username }/.ssh/authorized_keys" +# key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" +# } +# } +# } diff --git a/modules/etcd/output.tf b/modules/etcd/output.tf index 1e7929a..712a351 100644 --- a/modules/etcd/output.tf +++ b/modules/etcd/output.tf @@ -1,8 +1,8 @@ -output "external-lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } -output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } +# output "external-lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } +# output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } -# output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } -# output "external-elb" { value = "${ aws_elb.external.dns_name }" } -# output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } -output "master-ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } +# # output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } +# # output "external-elb" { value = "${ aws_elb.external.dns_name }" } +# # output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } +# output "master-ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } From 11ac184236f7af88681f3f5eefe71c38b4c1963a Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 05:42:42 +1200 Subject: [PATCH 072/149] Don't Assign Public IP to ETCD Nodes --- modules/etcd/nodes.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index e0d2fb4..48c4591 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -7,7 +7,7 @@ resource "google_compute_instance" "cncf" { tags = ["foo", "bar"] disk { - image = "debian-cloud/debian-8" + image = "coreos-stable-1298-7-0-v20170401" } // Local SSD disk @@ -22,6 +22,7 @@ resource "google_compute_instance" "cncf" { subnetwork_project = "${ var.project }" access_config { + // FIX ME Don't assign Public IP // Ephemeral IP } } From eba41d9e909788e3d93935d347769f8c8210d50d Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 08:06:24 +1200 Subject: [PATCH 073/149] Create Private DNS Zone for ETCD Cluster --- modules.tf | 12 +- modules/dns/dns.tf | 274 +++++++++++++++++++++--------------------- modules/dns/input.tf | 3 +- modules/dns/output.tf | 6 +- 4 files changed, 150 insertions(+), 145 deletions(-) diff --git a/modules.tf b/modules.tf index a2d6310..e5b4be7 100644 --- a/modules.tf +++ b/modules.tf @@ -6,12 +6,12 @@ module "vpc" { region = "${ var.region }" } -# module "dns" { -# source = "./modules/dns" -# name = "${ var.name }" -# internal-tld = "${ var.internal-tld }" -# master-ips = "${ module.etcd.master-ips }" -# } +module "dns" { + source = "./modules/dns" + name = "${ var.name }" + internal-tld = "${ var.internal-tld }" + # master-ips = "${ module.etcd.master-ips }" +} module "etcd" { source = "./modules/etcd" diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index c04064d..eca9b9e 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -1,136 +1,142 @@ -resource "azurerm_dns_zone" "cncf" { - name = "${ var.internal-tld }" - resource_group_name = "${ var.name }" +resource "google_dns_managed_zone" "cncf" { + name = "${ var.name }" + dns_name = "${ var.internal-tld }." + description = "${ var.name }" } -#Name Servers -resource "null_resource" "dns_dig" { - # Error running plan: 1 error(s) occurred: - # * module.dns.null_resource.ns_to_ip_list: null_resource.ns_to_ip_list: value of 'count' cannot be computed - # FIXME: hardcoding to 2 - # we only need 2 dns servers - count = 2 - depends_on = [ "azurerm_dns_zone.cncf" ] - provisioner "local-exec" { - # filename = "/dev/null" - command = < ${ var.name-servers-file }.${ count.index }.ip -EOF - } -} - -resource "null_resource" "dns_gen" { - depends_on = [ "null_resource.dns_dig" ] - provisioner "local-exec" { - # filename = "/dev/null" - command = < ${ var.name-servers-file} -EOF - } -} - -resource "azurerm_dns_a_record" "A-etcd" { - name = "etcd" - zone_name = "${azurerm_dns_zone.cncf.name}" - resource_group_name = "${ var.name }" - ttl = "300" - records = [ - "${ var.master-ips }" - ] -} - -resource "azurerm_dns_a_record" "A-etcds" { - count = "${ length(var.master-ips) }" - - name = "etcd${ count.index+1 }" - zone_name = "${azurerm_dns_zone.cncf.name}" - resource_group_name = "${ var.name }" - ttl = "300" - records = [ - "${ element(var.master-ips, count.index) }" - ] -} - -resource "azurerm_dns_a_record" "A-master" { - name = "master" - zone_name = "${azurerm_dns_zone.cncf.name}" - resource_group_name = "${ var.name }" - ttl = "300" - records = [ "${ var.master-ips }" ] -} - -resource "azurerm_dns_a_record" "A-masters" { - count = "${ length(var.master-ips) }" - name = "master${ count.index+1 }" - zone_name = "${azurerm_dns_zone.cncf.name}" - resource_group_name = "${ var.name }" - ttl = "300" - records = [ - "${ element(var.master-ips, count.index) }" - ] -} - -resource "azurerm_dns_srv_record" "etcd-client-tcp" { - name = "_etcd-client._tcp" - zone_name = "${azurerm_dns_zone.cncf.name}" - resource_group_name = "${ var.name }" - ttl = "300" - - record { - priority = 0 - weight = 0 - port = 2379 - target = "etcd1.${ var.internal-tld }" - } - - record { - priority = 0 - weight = 0 - port = 2379 - target = "etcd2.${ var.internal-tld }" - } - - record { - priority = 0 - weight = 0 - port = 2379 - target = "etcd3.${ var.internal-tld }" - } - -} - -resource "azurerm_dns_srv_record" "etcd-server-tcp" { - name = "_etcd-server-ssl._tcp" - zone_name = "${azurerm_dns_zone.cncf.name}" - resource_group_name = "${ var.name }" - ttl = "300" - - record { - priority = 0 - weight = 0 - port = 2380 - target = "etcd1.${ var.internal-tld }" - } - - record { - priority = 0 - weight = 0 - port = 2380 - target = "etcd2.${ var.internal-tld }" - } - - record { - priority = 0 - weight = 0 - port = 2380 - target = "etcd3.${ var.internal-tld }" - } - -} +# ns_zone" "cncf" { +# name = "${ var.internal-tld }" +# resource_group_name = "${ var.name }" +# } + +# #Name Servers +# resource "null_resource" "dns_dig" { +# # Error running plan: 1 error(s) occurred: +# # * module.dns.null_resource.ns_to_ip_list: null_resource.ns_to_ip_list: value of 'count' cannot be computed +# # FIXME: hardcoding to 2 +# # we only need 2 dns servers +# count = 2 +# depends_on = [ "azurerm_dns_zone.cncf" ] +# provisioner "local-exec" { +# # filename = "/dev/null" +# command = < ${ var.name-servers-file }.${ count.index }.ip +# EOF +# } +# } + +# resource "null_resource" "dns_gen" { +# depends_on = [ "null_resource.dns_dig" ] +# provisioner "local-exec" { +# # filename = "/dev/null" +# command = < ${ var.name-servers-file} +# EOF +# } +# } + +# resource "azurerm_dns_a_record" "A-etcd" { +# name = "etcd" +# zone_name = "${azurerm_dns_zone.cncf.name}" +# resource_group_name = "${ var.name }" +# ttl = "300" +# records = [ +# "${ var.master-ips }" +# ] +# } + +# resource "azurerm_dns_a_record" "A-etcds" { +# count = "${ length(var.master-ips) }" + +# name = "etcd${ count.index+1 }" +# zone_name = "${azurerm_dns_zone.cncf.name}" +# resource_group_name = "${ var.name }" +# ttl = "300" +# records = [ +# "${ element(var.master-ips, count.index) }" +# ] +# } + +# resource "azurerm_dns_a_record" "A-master" { +# name = "master" +# zone_name = "${azurerm_dns_zone.cncf.name}" +# resource_group_name = "${ var.name }" +# ttl = "300" +# records = [ "${ var.master-ips }" ] +# } + +# resource "azurerm_dns_a_record" "A-masters" { +# count = "${ length(var.master-ips) }" +# name = "master${ count.index+1 }" +# zone_name = "${azurerm_dns_zone.cncf.name}" +# resource_group_name = "${ var.name }" +# ttl = "300" +# records = [ +# "${ element(var.master-ips, count.index) }" +# ] +# } + +# resource "azurerm_dns_srv_record" "etcd-client-tcp" { +# name = "_etcd-client._tcp" +# zone_name = "${azurerm_dns_zone.cncf.name}" +# resource_group_name = "${ var.name }" +# ttl = "300" + +# record { +# priority = 0 +# weight = 0 +# port = 2379 +# target = "etcd1.${ var.internal-tld }" +# } + +# record { +# priority = 0 +# weight = 0 +# port = 2379 +# target = "etcd2.${ var.internal-tld }" +# } + +# record { +# priority = 0 +# weight = 0 +# port = 2379 +# target = "etcd3.${ var.internal-tld }" +# } + +# } + +# resource "azurerm_dns_srv_record" "etcd-server-tcp" { +# name = "_etcd-server-ssl._tcp" +# zone_name = "${azurerm_dns_zone.cncf.name}" +# resource_group_name = "${ var.name }" +# ttl = "300" + +# record { +# priority = 0 +# weight = 0 +# port = 2380 +# target = "etcd1.${ var.internal-tld }" +# } + +# record { +# priority = 0 +# weight = 0 +# port = 2380 +# target = "etcd2.${ var.internal-tld }" +# } + +# record { +# priority = 0 +# weight = 0 +# port = 2380 +# target = "etcd3.${ var.internal-tld }" +# } + +# } diff --git a/modules/dns/input.tf b/modules/dns/input.tf index 6d4bb71..24f011a 100644 --- a/modules/dns/input.tf +++ b/modules/dns/input.tf @@ -1,6 +1,5 @@ # variable "depends-id" {} variable "internal-tld" {} variable "name" {} -variable "name-servers-file" { default = "azure_dns_zone"} -variable "master-ips" { type = "list" } +# variable "master-ips" { type = "list" } # variable "vpc-id" {} diff --git a/modules/dns/output.tf b/modules/dns/output.tf index e1809b4..bd8187e 100644 --- a/modules/dns/output.tf +++ b/modules/dns/output.tf @@ -1,4 +1,4 @@ # output "depends-id" { value = "${null_resource.dummy_dependency.id}" } -output "internal-name-servers" { value = "${ azurerm_dns_zone.cncf.name_servers }" } -output "internal-zone-id" { value = "${ azurerm_dns_zone.cncf.zone_id }" } -output "name-servers-file" { value = "${ var.name-servers-file }" } +# output "internal-name-servers" { value = "${ azurerm_dns_zone.cncf.name_servers }" } +# output "internal-zone-id" { value = "${ azurerm_dns_zone.cncf.zone_id }" } +# output "name-servers-file" { value = "${ var.name-servers-file }" } From 150b2a2d680409ecef286287ebdfd083aa1b21bf Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 10:18:19 +1200 Subject: [PATCH 074/149] Output all ETCD Node IPs and Create a Single DNS Record that points to all ETCD Nodes for Cluster Discovery --- modules.tf | 2 +- modules/dns/dns.tf | 45 ++++++++++-------------------------------- modules/dns/input.tf | 2 +- modules/etcd/output.tf | 2 +- 4 files changed, 13 insertions(+), 38 deletions(-) diff --git a/modules.tf b/modules.tf index e5b4be7..41e3c97 100644 --- a/modules.tf +++ b/modules.tf @@ -10,7 +10,7 @@ module "dns" { source = "./modules/dns" name = "${ var.name }" internal-tld = "${ var.internal-tld }" - # master-ips = "${ module.etcd.master-ips }" + master-ips = "${ module.etcd.master-ips }" } module "etcd" { diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index eca9b9e..2a6375c 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -4,43 +4,18 @@ resource "google_dns_managed_zone" "cncf" { description = "${ var.name }" } -# ns_zone" "cncf" { -# name = "${ var.internal-tld }" -# resource_group_name = "${ var.name }" -# } +resource "google_dns_record_set" "A-etcd" { + name = "${ var.name }.${ var.internal-tld }." + type = "A" + ttl = 300 -# #Name Servers -# resource "null_resource" "dns_dig" { -# # Error running plan: 1 error(s) occurred: -# # * module.dns.null_resource.ns_to_ip_list: null_resource.ns_to_ip_list: value of 'count' cannot be computed -# # FIXME: hardcoding to 2 -# # we only need 2 dns servers -# count = 2 -# depends_on = [ "azurerm_dns_zone.cncf" ] -# provisioner "local-exec" { -# # filename = "/dev/null" -# command = < ${ var.name-servers-file }.${ count.index }.ip -# EOF -# } -# } + managed_zone = "${ google_dns_managed_zone.cncf.name }" + + rrdatas = [ + "${ var.master-ips }" + ] +} -# resource "null_resource" "dns_gen" { -# depends_on = [ "null_resource.dns_dig" ] -# provisioner "local-exec" { -# # filename = "/dev/null" -# command = < ${ var.name-servers-file} -# EOF -# } -# } # resource "azurerm_dns_a_record" "A-etcd" { # name = "etcd" diff --git a/modules/dns/input.tf b/modules/dns/input.tf index 24f011a..82b15d9 100644 --- a/modules/dns/input.tf +++ b/modules/dns/input.tf @@ -1,5 +1,5 @@ # variable "depends-id" {} variable "internal-tld" {} variable "name" {} -# variable "master-ips" { type = "list" } +variable "master-ips" { type = "list" } # variable "vpc-id" {} diff --git a/modules/etcd/output.tf b/modules/etcd/output.tf index 712a351..ee3e32d 100644 --- a/modules/etcd/output.tf +++ b/modules/etcd/output.tf @@ -5,4 +5,4 @@ # # output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } # # output "external-elb" { value = "${ aws_elb.external.dns_name }" } # # output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } -# output "master-ips" { value = ["${ azurerm_network_interface.cncf.*.private_ip_address }"] } +output "master-ips" { value = ["${ google_compute_instance.cncf.*.network_interface.0.address }"] } From 0991eabde1b1b54c1648bac214b8d6b60eac2086 Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 10:42:13 +1200 Subject: [PATCH 075/149] Create an A Record for Each ETCD Node Based on Count --- modules.tf | 1 + modules/dns/dns.tf | 13 +++++++++++++ modules/dns/input.tf | 3 +++ 3 files changed, 17 insertions(+) diff --git a/modules.tf b/modules.tf index 41e3c97..06ec8ad 100644 --- a/modules.tf +++ b/modules.tf @@ -11,6 +11,7 @@ module "dns" { name = "${ var.name }" internal-tld = "${ var.internal-tld }" master-ips = "${ module.etcd.master-ips }" + master-node-count = "${ var.master-node-count }" } module "etcd" { diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index 2a6375c..4da63f1 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -16,6 +16,19 @@ resource "google_dns_record_set" "A-etcd" { ] } +resource "google_dns_record_set" "A-etcds" { + count = "${ var.master-node-count }" + name = "${ var.name }${ count.index+1 }.${ var.internal-tld }." + type = "A" + ttl = 300 + + managed_zone = "${ google_dns_managed_zone.cncf.name }" + + rrdatas = [ + "${ element(var.master-ips, count.index) }" + ] +} + # resource "azurerm_dns_a_record" "A-etcd" { # name = "etcd" diff --git a/modules/dns/input.tf b/modules/dns/input.tf index 82b15d9..34adcac 100644 --- a/modules/dns/input.tf +++ b/modules/dns/input.tf @@ -2,4 +2,7 @@ variable "internal-tld" {} variable "name" {} variable "master-ips" { type = "list" } +variable "master-node-count" { default = "3" } + + # variable "vpc-id" {} From 7c3e59da2c292249aace68cf7d02f61ad150800d Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 11:14:18 +1200 Subject: [PATCH 076/149] Create SRV Records for ETCD Cluster --- modules/dns/dns.tf | 118 +++++++++++---------------------------------- 1 file changed, 27 insertions(+), 91 deletions(-) diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index 4da63f1..2fcc8d8 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -29,102 +29,38 @@ resource "google_dns_record_set" "A-etcds" { ] } +resource "google_dns_record_set" "CNAME-master" { + name = "master_${ var.name }.${ var.internal-tld }." + type = "CNAME" + ttl = 300 -# resource "azurerm_dns_a_record" "A-etcd" { -# name = "etcd" -# zone_name = "${azurerm_dns_zone.cncf.name}" -# resource_group_name = "${ var.name }" -# ttl = "300" -# records = [ -# "${ var.master-ips }" -# ] -# } - -# resource "azurerm_dns_a_record" "A-etcds" { -# count = "${ length(var.master-ips) }" - -# name = "etcd${ count.index+1 }" -# zone_name = "${azurerm_dns_zone.cncf.name}" -# resource_group_name = "${ var.name }" -# ttl = "300" -# records = [ -# "${ element(var.master-ips, count.index) }" -# ] -# } - -# resource "azurerm_dns_a_record" "A-master" { -# name = "master" -# zone_name = "${azurerm_dns_zone.cncf.name}" -# resource_group_name = "${ var.name }" -# ttl = "300" -# records = [ "${ var.master-ips }" ] -# } - -# resource "azurerm_dns_a_record" "A-masters" { -# count = "${ length(var.master-ips) }" -# name = "master${ count.index+1 }" -# zone_name = "${azurerm_dns_zone.cncf.name}" -# resource_group_name = "${ var.name }" -# ttl = "300" -# records = [ -# "${ element(var.master-ips, count.index) }" -# ] -# } - -# resource "azurerm_dns_srv_record" "etcd-client-tcp" { -# name = "_etcd-client._tcp" -# zone_name = "${azurerm_dns_zone.cncf.name}" -# resource_group_name = "${ var.name }" -# ttl = "300" - -# record { -# priority = 0 -# weight = 0 -# port = 2379 -# target = "etcd1.${ var.internal-tld }" -# } - -# record { -# priority = 0 -# weight = 0 -# port = 2379 -# target = "etcd2.${ var.internal-tld }" -# } + managed_zone = "${ google_dns_managed_zone.cncf.name }" -# record { -# priority = 0 -# weight = 0 -# port = 2379 -# target = "etcd3.${ var.internal-tld }" -# } + rrdatas = [ + "${ var.name }.${ var.internal-tld }." + ] +} -# } +resource "google_dns_record_set" "etcd-client-tcp" { + name = "_etcd-client._tcp.${ var.internal-tld }." + type = "SRV" + ttl = 300 -# resource "azurerm_dns_srv_record" "etcd-server-tcp" { -# name = "_etcd-server-ssl._tcp" -# zone_name = "${azurerm_dns_zone.cncf.name}" -# resource_group_name = "${ var.name }" -# ttl = "300" + managed_zone = "${ google_dns_managed_zone.cncf.name }" -# record { -# priority = 0 -# weight = 0 -# port = 2380 -# target = "etcd1.${ var.internal-tld }" -# } + rrdatas = [ + "${ formatlist("0 0 2379 %v", google_dns_record_set.A-etcds.*.name) }" + ] +} -# record { -# priority = 0 -# weight = 0 -# port = 2380 -# target = "etcd2.${ var.internal-tld }" -# } +resource "google_dns_record_set" "etcd-server-tcp" { + name = "_etcd-server-ssl._tcp.${ var.internal-tld }." + type = "SRV" + ttl = 300 -# record { -# priority = 0 -# weight = 0 -# port = 2380 -# target = "etcd3.${ var.internal-tld }" -# } + managed_zone = "${ google_dns_managed_zone.cncf.name }" -# } + rrdatas = [ + "${ formatlist("0 0 2380 %v", google_dns_record_set.A-etcds.*.name) }" + ] +} From 0f98b47695f61d070a3639e3dd0f3eca0e63d6ce Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 10 Apr 2017 12:44:19 +1200 Subject: [PATCH 077/149] Use Cloud INIT for ETCD Cluster Cration and Create a Bastion host for Debugging --- modules.tf | 50 ++++++------ modules/bastion/input.tf | 28 ++++--- modules/bastion/node.tf | 141 ++++++++++++++++++++++------------ modules/bastion/output.tf | 4 +- modules/etcd/cloud-config.tf | 41 +++++----- modules/etcd/cloud-config.yml | 9 +-- modules/etcd/input.tf | 16 ++-- modules/etcd/nodes.tf | 2 +- 8 files changed, 169 insertions(+), 122 deletions(-) diff --git a/modules.tf b/modules.tf index 06ec8ad..77b8ac4 100644 --- a/modules.tf +++ b/modules.tf @@ -32,36 +32,38 @@ module "dns" { # storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" # storage-container = "${ azurerm_storage_container.cncf.name }" # availability-id = "${ azurerm_availability_set.cncf.id }" -# cluster-domain = "${ var.cluster-domain }" -# kubelet-image-url = "${ var.kubelet-image-url }" -# kubelet-image-tag = "${ var.kubelet-image-tag }" -# dns-service-ip = "${ var.dns-service-ip }" -# internal-tld = "${ var.internal-tld }" -# pod-cidr = "${ var.pod-cidr }" -# service-cidr = "${ var.service-cidr }" -# k8s-apiserver-tar = "${file("${ var.data-dir }/.cfssl/k8s-apiserver.tar")}" + cluster-domain = "${ var.cluster-domain }" + kubelet-image-url = "${ var.kubelet-image-url }" + kubelet-image-tag = "${ var.kubelet-image-tag }" + dns-service-ip = "${ var.dns-service-ip }" + internal-tld = "${ var.internal-tld }" + pod-cidr = "${ var.pod-cidr }" + service-cidr = "${ var.service-cidr }" + k8s-apiserver-tar = "${file("${ var.data-dir }/.cfssl/k8s-apiserver.tar")}" # cloud-config = "${file("${ var.data-dir }/azure-config.json")}" # # etcd-security-group-id = "${ module.security.etcd-id }" # # external-elb-security-group-id = "${ module.security.external-elb-id }" } -# module "bastion" { -# source = "./modules/bastion" -# name = "${ var.name }" -# location = "${ var.location }" -# bastion-vm-size = "${ var.bastion-vm-size }" -# image-publisher = "${ var.image-publisher }" -# image-offer = "${ var.image-offer }" -# image-sku = "${ var.image-sku }" -# image-version = "${ var.image-version }" -# admin-username = "${ var.admin-username }" -# subnet-id = "${ module.vpc.subnet-id }" -# storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" -# storage-container = "${ azurerm_storage_container.cncf.name }" -# availability-id = "${ azurerm_availability_set.cncf.id }" -# internal-tld = "${ var.internal-tld }" -# } +module "bastion" { + source = "./modules/bastion" + name = "${ var.name }" + region = "${ var.region }" + zone = "${ var.zone }" + project = "${ var.project }" + # bastion-vm-size = "${ var.bastion-vm-size }" + # image-publisher = "${ var.image-publisher }" + # image-offer = "${ var.image-offer }" + # image-sku = "${ var.image-sku }" + # image-version = "${ var.image-version }" + # admin-username = "${ var.admin-username }" + # subnet-id = "${ module.vpc.subnet-id }" + # storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" + # storage-container = "${ azurerm_storage_container.cncf.name }" + # availability-id = "${ azurerm_availability_set.cncf.id }" + internal-tld = "${ var.internal-tld }" +} # module "worker" { # source = "./modules/worker" diff --git a/modules/bastion/input.tf b/modules/bastion/input.tf index 7cafb1a..f8b2160 100644 --- a/modules/bastion/input.tf +++ b/modules/bastion/input.tf @@ -1,16 +1,22 @@ variable "name" {} -variable "location" {} -variable "bastion-vm-size" {} -variable "image-publisher" {} -variable "image-offer" {} -variable "image-sku" {} -variable "image-version" {} -variable "admin-username" {} +# variable "location" {} +variable "region" {} +variable "project" {} +variable "zone" {} +# variable "bastion-vm-size" {} +# variable "image-publisher" {} +# variable "image-offer" {} +# variable "image-sku" {} +# variable "image-version" {} +# variable "admin-username" {} variable "internal-tld" {} -variable "subnet-id" {} -variable "availability-id" {} -variable "storage-container" {} -variable "storage-primary-endpoint" {} +# variable "subnet-id" {} +# variable "availability-id" {} +# variable "storage-container" {} +# variable "storage-primary-endpoint" {} + + + # variable "cidr-allow-ssh" {} # variable "security-group-id" {} diff --git a/modules/bastion/node.tf b/modules/bastion/node.tf index 6611d79..2424465 100644 --- a/modules/bastion/node.tf +++ b/modules/bastion/node.tf @@ -1,66 +1,113 @@ -resource "azurerm_public_ip" "cncf" { - name = "PublicIPForBastion" - location = "${ var.location }" - resource_group_name = "${ var.name }" - public_ip_address_allocation = "static" - domain_name_label = "bastion${ var.name }" -} +resource "google_compute_instance" "cncf" { + name = "${ var.name }" + machine_type = "n1-standard-1" + zone = "${ var.zone }" -resource "azurerm_network_interface" "cncf" { - name = "${ var.name }" - location = "${ var.location }" - resource_group_name = "${ var.name }" + tags = ["foo", "bar"] - ip_configuration { - name = "${ var.name }" - subnet_id = "${ var.subnet-id }" - private_ip_address_allocation = "dynamic" - public_ip_address_id = "${ azurerm_public_ip.cncf.id }" + disk { + image = "coreos-stable-1298-7-0-v20170401" } -} -resource "azurerm_virtual_machine" "cncf" { - name = "${ var.name }" - location = "${ var.location }" - availability_set_id = "${ var.availability-id }" - resource_group_name = "${ var.name }" - network_interface_ids = ["${azurerm_network_interface.cncf.id}"] - vm_size = "${ var.bastion-vm-size }" - - storage_image_reference { - publisher = "${ var.image-publisher }" - offer = "${ var.image-offer }" - sku = "${ var.image-sku }" - version = "${ var.image-version}" + // Local SSD disk + disk { + type = "local-ssd" + scratch = true } - storage_os_disk { - name = "disk2" - vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/disk2.vhd" - caching = "ReadWrite" - create_option = "FromImage" + network_interface { + # network = "${ var.name }" + subnetwork = "${ var.name }" + subnetwork_project = "${ var.project }" + + access_config { + // FIX ME Don't assign Public IP + // Ephemeral IP + } } - os_profile { - computer_name = "hostname" - admin_username = "${ var.admin-username }" - admin_password = "Password1234!" - custom_data = "${ data.template_file.user-data.rendered }" - #custom_data = "${file("${path.module}/user-data2.yml")}" + metadata { + user-data = "${ data.template_file.user-data.rendered }" } - os_profile_linux_config { - disable_password_authentication = true - ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" - key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" - } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] } } data "template_file" "user-data" { template = "${ file( "${ path.module }/user-data.yml" )}" + vars { internal-tld = "${ var.internal-tld }" } } + + + +# resource "azurerm_public_ip" "cncf" { +# name = "PublicIPForBastion" +# location = "${ var.location }" +# resource_group_name = "${ var.name }" +# public_ip_address_allocation = "static" +# domain_name_label = "bastion${ var.name }" +# } + +# resource "azurerm_network_interface" "cncf" { +# name = "${ var.name }" +# location = "${ var.location }" +# resource_group_name = "${ var.name }" + +# ip_configuration { +# name = "${ var.name }" +# subnet_id = "${ var.subnet-id }" +# private_ip_address_allocation = "dynamic" +# public_ip_address_id = "${ azurerm_public_ip.cncf.id }" +# } +# } + +# resource "azurerm_virtual_machine" "cncf" { +# name = "${ var.name }" +# location = "${ var.location }" +# availability_set_id = "${ var.availability-id }" +# resource_group_name = "${ var.name }" +# network_interface_ids = ["${azurerm_network_interface.cncf.id}"] +# vm_size = "${ var.bastion-vm-size }" + +# storage_image_reference { +# publisher = "${ var.image-publisher }" +# offer = "${ var.image-offer }" +# sku = "${ var.image-sku }" +# version = "${ var.image-version}" +# } + +# storage_os_disk { +# name = "disk2" +# vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/disk2.vhd" +# caching = "ReadWrite" +# create_option = "FromImage" +# } + +# os_profile { +# computer_name = "hostname" +# admin_username = "${ var.admin-username }" +# admin_password = "Password1234!" +# custom_data = "${ data.template_file.user-data.rendered }" +# #custom_data = "${file("${path.module}/user-data2.yml")}" +# } + +# os_profile_linux_config { +# disable_password_authentication = true +# ssh_keys { +# path = "/home/${ var.admin-username }/.ssh/authorized_keys" +# key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" +# } +# } +# } + +# data "template_file" "user-data" { +# template = "${ file( "${ path.module }/user-data.yml" )}" +# vars { +# internal-tld = "${ var.internal-tld }" +# } +# } diff --git a/modules/bastion/output.tf b/modules/bastion/output.tf index b97729b..aefde0f 100644 --- a/modules/bastion/output.tf +++ b/modules/bastion/output.tf @@ -1,2 +1,2 @@ -output "bastion-ip" { value = "${azurerm_public_ip.cncf.ip_address}" } -output "bastion-fqdn" { value = "${azurerm_public_ip.cncf.fqdn}" } +# output "bastion-ip" { value = "${azurerm_public_ip.cncf.ip_address}" } +# output "bastion-fqdn" { value = "${azurerm_public_ip.cncf.fqdn}" } diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index c78399c..0569a9d 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -1,24 +1,21 @@ -# data "template_file" "cloud-config" { -# count = "${ var.master-node-count }" -# template = "${ file( "${ path.module }/cloud-config.yml" )}" +data "template_file" "cloud-config" { + count = "${ var.master-node-count }" + template = "${ file( "${ path.module }/cloud-config.yml" )}" -# vars { -# # bucket = "${ var.bucket-prefix }" -# cluster-domain = "${ var.cluster-domain }" -# cluster-token = "etcd-cluster-${ var.name }" -# dns-service-ip = "${ var.dns-service-ip }" -# etc-tar = "/manifests/etc.tar" -# fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" -# hostname = "etcd${ count.index + 1 }" -# kubelet-image-url = "${ var.kubelet-image-url }" -# kubelet-image-tag = "${ var.kubelet-image-tag }" -# internal-tld = "${ var.internal-tld }" -# pod-cidr = "${ var.pod-cidr }" -# location = "${ var.location }" -# service-cidr = "${ var.service-cidr }" -# k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" -# node-ip = "${ element(azurerm_network_interface.cncf.*.private_ip_address, count.index) }" -# cloud-config = "${ base64encode(var.cloud-config) }" + vars { + cluster-domain = "${ var.cluster-domain }" + cluster-token = "etcd-cluster-${ var.name }" + dns-service-ip = "${ var.dns-service-ip }" + etc-tar = "/manifests/etc.tar" + fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" + hostname = "etcd${ count.index + 1 }" + kubelet-image-url = "${ var.kubelet-image-url }" + kubelet-image-tag = "${ var.kubelet-image-tag }" + internal-tld = "${ var.internal-tld }" + pod-cidr = "${ var.pod-cidr }" + service-cidr = "${ var.service-cidr }" + k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" + # cloud-config = "${ base64encode(var.cloud-config) }" -# } -# } + } +} diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index 3f67928..6a515e5 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -12,8 +12,8 @@ coreos: initial-cluster-state: new initial-cluster-token: ${ cluster-token } # key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem - listen-client-urls: http://${ node-ip }:2379,http://127.0.0.1:2379 - listen-peer-urls: https://${ node-ip }:2380 + listen-client-urls: http://${ fqdn }:2379,http://127.0.0.1:2379 + listen-peer-urls: https://${ fqdn }:2380 name: ${ hostname } peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem peer-client-cert-auth: true @@ -273,8 +273,3 @@ write-files: - encoding: b64 content: ${ k8s-apiserver-tar } path: /etc/kubernetes/ssl/k8s-apiserver.tar - - - encoding: b64 - content: ${ cloud-config } - path: /etc/kubernetes/ssl/azure-config.json - diff --git a/modules/etcd/input.tf b/modules/etcd/input.tf index 35b36ad..a77acf1 100644 --- a/modules/etcd/input.tf +++ b/modules/etcd/input.tf @@ -14,15 +14,15 @@ variable "master-node-count" {} # variable "storage-account" {} # variable "storage-primary-endpoint" {} # variable "storage-container" {} -# variable "k8s-apiserver-tar" {} -# variable "cluster-domain" {} -# variable "dns-service-ip" {} -# variable "internal-tld" {} -# variable "pod-cidr" {} -# variable "service-cidr" {} +variable "k8s-apiserver-tar" {} +variable "cluster-domain" {} +variable "dns-service-ip" {} +variable "internal-tld" {} +variable "pod-cidr" {} +variable "service-cidr" {} # variable "admin-username" {} -# variable "kubelet-image-url" {} -# variable "kubelet-image-tag" {} +variable "kubelet-image-url" {} +variable "kubelet-image-tag" {} # variable "cloud-config" {} # variable "etcd-security-group-id" {} diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index 48c4591..2c60758 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -28,7 +28,7 @@ resource "google_compute_instance" "cncf" { } metadata { - foo = "bar" + user-data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" } service_account { From ce9d43ac6d6746a497bbfda4662e870d09d004fe Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 11 Apr 2017 09:23:11 +1200 Subject: [PATCH 078/149] First attempt at using cloudinit_template to gzip encode to get below size limit and create Bastion node --- modules/bastion/user-data.yml | 2 ++ modules/etcd/cloud-config.tf | 21 ++++++++++++++++++++- modules/etcd/cloud-config.yml | 6 +++--- modules/etcd/nodes.tf | 2 +- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/modules/bastion/user-data.yml b/modules/bastion/user-data.yml index db22516..ca73849 100644 --- a/modules/bastion/user-data.yml +++ b/modules/bastion/user-data.yml @@ -24,3 +24,5 @@ coreos: # ExecStartPre=/usr/bin/curl -L -o /opt/bin/s3-iam-get \ # https://raw.githubusercontent.com/kz8s/s3-iam-get/master/s3-iam-get # ExecStart=/usr/bin/chmod +x /opt/bin/s3-iam-get +ssh_authorized_keys: + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnxGbWpxBQoomQ4hD4eO5HnxM9olcoZbs70hMWHw/e5aErfyqp770GNdNe2HTWOV4pcJewCfo/F0I4xm5HIEVlEE4ml45nBMwiy9btHQ47nyXxrSln+WPpDdnv2/H154JQgrmDCrU3uj2sQGaLFGKsVZ08+XfefVC/uG056GQxco2mA/aASZ7eudPZ4lP6/Hw2O3Qz+dUW0QhYPfCUCfvb80AKpGRAwPoiWsB4AfVUKD1mXrkmVMs4LMT4GJAoIgxKcOxw5796m1YdUdsxAW62fUpO2+xUsjw7FD5HqfckqsNyijKWt9E0gZAtNDAIsOe/HGc7B9M31WK3Fy7FQjep root@twt084pc" diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index 0569a9d..6f95365 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -1,5 +1,5 @@ data "template_file" "cloud-config" { - count = "${ var.master-node-count }" + # count = "${ var.master-node-count }" template = "${ file( "${ path.module }/cloud-config.yml" )}" vars { @@ -19,3 +19,22 @@ data "template_file" "cloud-config" { } } + +data "template_cloudinit_config" "myconfig" { + count = "${ var.master-node-count }" + gzip = false + base64_encode = false + + part { + content = "${ data.template_file.cloud-config.rendered }" + } + + part { + content = "${ var.k8s-apiserver-tar }" + } +} + + + # output "cloud-init-cruft" { +# value = "${data.template_cloudinit_config.myconfig.0.rendered}" +# } diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index 6a515e5..9575299 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -270,6 +270,6 @@ write-files: copytruncate } - - encoding: b64 - content: ${ k8s-apiserver-tar } - path: /etc/kubernetes/ssl/k8s-apiserver.tar + - path: /etc/kubernetes/ssl/k8s-apiserver.tar + content: | + diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index 2c60758..a613009 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -28,7 +28,7 @@ resource "google_compute_instance" "cncf" { } metadata { - user-data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" + user-data = "${ element(data.template_cloudinit_config.myconfig.*.rendered, count.index) }" } service_account { From b656791450b0cce732864fd8a80b20aa088f5a33 Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 11 Apr 2017 12:22:12 +1200 Subject: [PATCH 079/149] Revert Cloud-init Changes --- modules/bastion/user-data.yml | 14 -------------- modules/etcd/cloud-config.tf | 20 +------------------- modules/etcd/cloud-config.yml | 3 --- modules/etcd/nodes.tf | 2 +- modules/worker/cloud-config.yml | 7 ------- 5 files changed, 2 insertions(+), 44 deletions(-) diff --git a/modules/bastion/user-data.yml b/modules/bastion/user-data.yml index ca73849..b12683a 100644 --- a/modules/bastion/user-data.yml +++ b/modules/bastion/user-data.yml @@ -12,17 +12,3 @@ coreos: units: - name: etcd2.service command: start - # - name: s3-iam-get.service - # command: start - # content: | - # [Unit] - # Description=s3-iam-get - # [Service] - # Type=oneshot - # RemainAfterExit=yes - # ExecStartPre=-/usr/bin/mkdir -p /opt/bin - # ExecStartPre=/usr/bin/curl -L -o /opt/bin/s3-iam-get \ - # https://raw.githubusercontent.com/kz8s/s3-iam-get/master/s3-iam-get - # ExecStart=/usr/bin/chmod +x /opt/bin/s3-iam-get -ssh_authorized_keys: - - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnxGbWpxBQoomQ4hD4eO5HnxM9olcoZbs70hMWHw/e5aErfyqp770GNdNe2HTWOV4pcJewCfo/F0I4xm5HIEVlEE4ml45nBMwiy9btHQ47nyXxrSln+WPpDdnv2/H154JQgrmDCrU3uj2sQGaLFGKsVZ08+XfefVC/uG056GQxco2mA/aASZ7eudPZ4lP6/Hw2O3Qz+dUW0QhYPfCUCfvb80AKpGRAwPoiWsB4AfVUKD1mXrkmVMs4LMT4GJAoIgxKcOxw5796m1YdUdsxAW62fUpO2+xUsjw7FD5HqfckqsNyijKWt9E0gZAtNDAIsOe/HGc7B9M31WK3Fy7FQjep root@twt084pc" diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index 6f95365..84072de 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -1,5 +1,5 @@ data "template_file" "cloud-config" { - # count = "${ var.master-node-count }" + count = "${ var.master-node-count }" template = "${ file( "${ path.module }/cloud-config.yml" )}" vars { @@ -20,21 +20,3 @@ data "template_file" "cloud-config" { } } -data "template_cloudinit_config" "myconfig" { - count = "${ var.master-node-count }" - gzip = false - base64_encode = false - - part { - content = "${ data.template_file.cloud-config.rendered }" - } - - part { - content = "${ var.k8s-apiserver-tar }" - } -} - - - # output "cloud-init-cruft" { -# value = "${data.template_cloudinit_config.myconfig.0.rendered}" -# } diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index 9575299..938ada8 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -270,6 +270,3 @@ write-files: copytruncate } - - path: /etc/kubernetes/ssl/k8s-apiserver.tar - content: | - diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index a613009..2c60758 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -28,7 +28,7 @@ resource "google_compute_instance" "cncf" { } metadata { - user-data = "${ element(data.template_cloudinit_config.myconfig.*.rendered, count.index) }" + user-data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" } service_account { diff --git a/modules/worker/cloud-config.yml b/modules/worker/cloud-config.yml index 36d1937..6f4258e 100644 --- a/modules/worker/cloud-config.yml +++ b/modules/worker/cloud-config.yml @@ -149,10 +149,3 @@ write-files: copytruncate } - - encoding: b64 - content: ${ k8s-worker-tar } - path: /etc/kubernetes/ssl/k8s-worker.tar - - - encoding: b64 - content: ${ cloud-config } - path: /etc/kubernetes/ssl/azure-config.json From a360c2dbc5dc927ab21d906208b0b9019fc6d659 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 13 Apr 2017 05:47:35 +1200 Subject: [PATCH 080/149] encode certs with base64 to meet cloud-init metadata size constraints --- modules.tf | 6 +++++- modules/etcd/cloud-config.tf | 6 +++++- modules/etcd/cloud-config.yml | 20 ++++++++++++++++++++ modules/etcd/input.tf | 6 +++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/modules.tf b/modules.tf index 77b8ac4..b5be7b2 100644 --- a/modules.tf +++ b/modules.tf @@ -39,7 +39,11 @@ module "dns" { internal-tld = "${ var.internal-tld }" pod-cidr = "${ var.pod-cidr }" service-cidr = "${ var.service-cidr }" - k8s-apiserver-tar = "${file("${ var.data-dir }/.cfssl/k8s-apiserver.tar")}" + k8s-apiserver-key = "${file("${ var.data-dir }/.cfssl/k8s-apiserver-key.pem")}" + k8s-apiserver = "${file("${ var.data-dir }/.cfssl/k8s-apiserver.pem")}" + k8s-etcd-key = "${file("${ var.data-dir }/.cfssl/k8s-etcd-key.pem")}" + k8s-etcd = "${file("${ var.data-dir }/.cfssl/k8s-etcd.pem")}" + ca = "${file("${ var.data-dir }/.cfssl/ca.pem")}" # cloud-config = "${file("${ var.data-dir }/azure-config.json")}" # # etcd-security-group-id = "${ module.security.etcd-id }" # # external-elb-security-group-id = "${ module.security.external-elb-id }" diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index 84072de..8765022 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -14,7 +14,11 @@ data "template_file" "cloud-config" { internal-tld = "${ var.internal-tld }" pod-cidr = "${ var.pod-cidr }" service-cidr = "${ var.service-cidr }" - k8s-apiserver-tar = "${ base64encode(var.k8s-apiserver-tar) }" + ca = "${ base64encode(var.ca) }" + k8s-etcd = "${ base64encode(var.k8s-etcd) }" + k8s-etcd-key = "${ base64encode(var.k8s-etcd-key) }" + k8s-apiserver = "${ base64encode(var.k8s-apiserver) }" + k8s-apiserver-key = "${ base64encode(var.k8s-apiserver-key) }" # cloud-config = "${ base64encode(var.cloud-config) }" } diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index 938ada8..196f04d 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -270,3 +270,23 @@ write-files: copytruncate } + - encoding: b64 + content: "${ ca }" + path: /etc/kubernetes/ssl/ca.pem + + - encoding: b64 + content: "${ k8s-etcd }" + path: /etc/kubernetes/ssl/k8s-etcd.pem + + - encoding: b64 + content: "${ k8s-etcd-key }" + path: /etc/kubernetes/ssl/k8s-etcd-key.pem + + - encoding: b64 + content: "${ k8s-apiserver }" + path: /etc/kubernetes/ssl/k8s-apiserver.pem + + - encoding: b64 + content: "${ k8s-apiserver-key }" + path: /etc/kubernetes/ssl/k8s-apiserver-key.pem + diff --git a/modules/etcd/input.tf b/modules/etcd/input.tf index a77acf1..762dfd0 100644 --- a/modules/etcd/input.tf +++ b/modules/etcd/input.tf @@ -14,7 +14,11 @@ variable "master-node-count" {} # variable "storage-account" {} # variable "storage-primary-endpoint" {} # variable "storage-container" {} -variable "k8s-apiserver-tar" {} +variable "k8s-apiserver-key" {} +variable "k8s-apiserver" {} +variable "k8s-etcd-key" {} +variable "k8s-etcd" {} +variable "ca" {} variable "cluster-domain" {} variable "dns-service-ip" {} variable "internal-tld" {} From b5860c7e5a2d3a67b7853ca57240b5dd52ea5f0d Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 13 Apr 2017 06:05:24 +1200 Subject: [PATCH 081/149] Write Master Certs to Disk --- modules.tf | 56 ++++++----- modules/worker/cloud-config.tf | 5 +- modules/worker/cloud-config.yml | 11 +++ modules/worker/input.tf | 4 +- modules/worker/nodes.tf | 170 ++++++++++++-------------------- 5 files changed, 112 insertions(+), 134 deletions(-) diff --git a/modules.tf b/modules.tf index b5be7b2..04460a1 100644 --- a/modules.tf +++ b/modules.tf @@ -69,32 +69,36 @@ module "bastion" { internal-tld = "${ var.internal-tld }" } -# module "worker" { -# source = "./modules/worker" -# name = "${ var.name }" -# location = "${ var.location }" -# admin-username = "${ var.admin-username }" -# worker-node-count = "${ var.worker-node-count }" -# worker-vm-size = "${ var.worker-vm-size }" -# image-publisher = "${ var.image-publisher }" -# image-offer = "${ var.image-offer }" -# image-sku = "${ var.image-sku }" -# image-version = "${ var.image-version }" -# subnet-id = "${ module.vpc.subnet-id }" -# storage-account = "${ azurerm_storage_account.cncf.name }" -# storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" -# storage-container = "${ azurerm_storage_container.cncf.name }" -# availability-id = "${ azurerm_availability_set.cncf.id }" -# external-lb = "${ module.etcd.external-lb }" -# cluster-domain = "${ var.cluster-domain }" -# kubelet-image-url = "${ var.kubelet-image-url }" -# kubelet-image-tag = "${ var.kubelet-image-tag }" -# dns-service-ip = "${ var.dns-service-ip }" -# internal-tld = "${ var.internal-tld }" -# k8s-worker-tar = "${file("${ var.data-dir }/.cfssl/k8s-worker.tar")}" -# cloud-config = "${file("${ var.data-dir }/azure-config.json")}" -# # security-group-id = "${ module.security.worker-id }" -# } +module "worker" { + source = "./modules/worker" + name = "${ var.name }" + region = "${ var.region }" + zone = "${ var.zone }" + project = "${ var.project }" + # admin-username = "${ var.admin-username }" + worker-node-count = "${ var.worker-node-count }" + # worker-vm-size = "${ var.worker-vm-size }" + # image-publisher = "${ var.image-publisher }" + # image-offer = "${ var.image-offer }" + # image-sku = "${ var.image-sku }" + # image-version = "${ var.image-version }" + # subnet-id = "${ module.vpc.subnet-id }" + # storage-account = "${ azurerm_storage_account.cncf.name }" + # storage-primary-endpoint = "${ azurerm_storage_account.cncf.primary_blob_endpoint }" + # storage-container = "${ azurerm_storage_container.cncf.name }" + # availability-id = "${ azurerm_availability_set.cncf.id }" + # external-lb = "${ module.etcd.external-lb }" + cluster-domain = "${ var.cluster-domain }" + kubelet-image-url = "${ var.kubelet-image-url }" + kubelet-image-tag = "${ var.kubelet-image-tag }" + dns-service-ip = "${ var.dns-service-ip }" + internal-tld = "${ var.internal-tld }" + ca = "${file("${ var.data-dir }/.cfssl/ca.pem")}" + k8s-worker = "${file("${ var.data-dir }/.cfssl/k8s-worker.pem")}" + k8s-worker-key = "${file("${ var.data-dir }/.cfssl/k8s-worker-key.pem")}" + # cloud-config = "${file("${ var.data-dir }/azure-config.json")}" + # security-group-id = "${ module.security.worker-id }" +} # module "kubeconfig" { diff --git a/modules/worker/cloud-config.tf b/modules/worker/cloud-config.tf index 76fa63d..94b7e97 100644 --- a/modules/worker/cloud-config.tf +++ b/modules/worker/cloud-config.tf @@ -7,8 +7,9 @@ data "template_file" "cloud-config" { kubelet-image-url = "${ var.kubelet-image-url }" kubelet-image-tag = "${ var.kubelet-image-tag }" internal-tld = "${ var.internal-tld }" - location = "${ var.location }" - k8s-worker-tar = "${ base64encode(var.k8s-worker-tar) }" + ca = "${ var.ca }" + k8s-worker = "${ var.k8s-worker }" + k8s-worker-key = "${ var.k8s-worker-key }" cloud-config = "${ base64encode(var.cloud-config) }" } } diff --git a/modules/worker/cloud-config.yml b/modules/worker/cloud-config.yml index 6f4258e..f54fdda 100644 --- a/modules/worker/cloud-config.yml +++ b/modules/worker/cloud-config.yml @@ -149,3 +149,14 @@ write-files: copytruncate } + - encoding: b64 + content: "${ ca }" + path: /etc/kubernetes/ssl/ca.pem + + - encoding: b64 + content: "${ k8s-worker }" + path: /etc/kubernetes/ssl/k8s-worker.pem + + - encoding: b64 + content: "${ k8s-worker-key }" + path: /etc/kubernetes/ssl/k8s-worker-key.pem diff --git a/modules/worker/input.tf b/modules/worker/input.tf index e5d9d19..e02c5d1 100644 --- a/modules/worker/input.tf +++ b/modules/worker/input.tf @@ -16,7 +16,9 @@ variable "cluster-domain" {} variable "dns-service-ip" {} variable "internal-tld" {} variable "admin-username" {} -variable "k8s-worker-tar" {} variable "kubelet-image-url" {} variable "kubelet-image-tag" {} +variable "ca" {} +variable "k8s-worker" {} +variable "k8s-worker-key" {} variable "cloud-config" {} diff --git a/modules/worker/nodes.tf b/modules/worker/nodes.tf index 42bde3f..9fc4129 100644 --- a/modules/worker/nodes.tf +++ b/modules/worker/nodes.tf @@ -1,105 +1,65 @@ -resource "azurerm_network_interface" "cncf" { - count = "${ var.worker-node-count }" - name = "worker-interface${ count.index + 1 }" - location = "${ var.location }" - resource_group_name = "${ var.name }" - - ip_configuration { - name = "worker-nic${ count.index + 1 }" - subnet_id = "${ var.subnet-id }" - private_ip_address_allocation = "dynamic" - } -} - -resource "azurerm_virtual_machine" "cncf" { - count = "${ var.worker-node-count }" - name = "worker-node${ count.index + 1 }" - location = "${ var.location }" - availability_set_id = "${ var.availability-id }" - resource_group_name = "${ var.name }" - network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] - vm_size = "${ var.worker-vm-size }" - - storage_image_reference { - publisher = "${ var.image-publisher }" - offer = "${ var.image-offer }" - sku = "${ var.image-sku }" - version = "${ var.image-version}" - } - - storage_os_disk { - name = "worker-disks${ count.index + 1 }" - vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/worker-vhd${ count.index + 1 }.vhd" - caching = "ReadWrite" - create_option = "FromImage" - } - - os_profile { - computer_name = "worker-node${ count.index + 1 }" - admin_username = "${ var.admin-username }" - admin_password = "Password1234!" - custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" - } - - os_profile_linux_config { - disable_password_authentication = true - ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" - key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" - } - } -} - -/* -resource "azurerm_virtual_machine_scale_set" "cncf" { - name = "${ var.name }" - location = "${ var.location }" - resource_group_name = "${ var.name }" - upgrade_policy_mode = "Manual" - - sku { - name = "Standard_A0" - tier = "Standard" - capacity = 2 - } - - os_profile { - computer_name_prefix = "worker" - admin_username = "${ var.admin-username }" - admin_password = "Password1234" - custom_data = "${ data.template_file.cloud-config.rendered }" - } - - os_profile_linux_config { - disable_password_authentication = true - ssh_keys { - path = "/home/${ var.admin-username }/.ssh/authorized_keys" - key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" - } - } - - network_profile { - name = "TestNetworkProfile" - primary = true - ip_configuration { - name = "TestIPConfiguration" - subnet_id = "${ var.subnet-id }" - # load_balancer_backend_address_pool_ids = ["${ var.external-lb }"] - } - } - - storage_profile_os_disk { - name = "osDiskProfile" - caching = "ReadWrite" - create_option = "FromImage" - vhd_containers = ["${ var.storage-primary-endpoint }${ var.storage-container }"] - } - - storage_profile_image_reference { - publisher = "CoreOS" - offer = "CoreOS" - sku = "Stable" - version = "1298.6.0" - } -} -*/ + + + + + + + + + + + + +# resource "azurerm_network_interface" "cncf" { +# count = "${ var.worker-node-count }" +# name = "worker-interface${ count.index + 1 }" +# location = "${ var.location }" +# resource_group_name = "${ var.name }" + +# ip_configuration { +# name = "worker-nic${ count.index + 1 }" +# subnet_id = "${ var.subnet-id }" +# private_ip_address_allocation = "dynamic" +# } +# } + +# resource "azurerm_virtual_machine" "cncf" { +# count = "${ var.worker-node-count }" +# name = "worker-node${ count.index + 1 }" +# location = "${ var.location }" +# availability_set_id = "${ var.availability-id }" +# resource_group_name = "${ var.name }" +# network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] +# vm_size = "${ var.worker-vm-size }" + +# storage_image_reference { +# publisher = "${ var.image-publisher }" +# offer = "${ var.image-offer }" +# sku = "${ var.image-sku }" +# version = "${ var.image-version}" +# } + +# storage_os_disk { +# name = "worker-disks${ count.index + 1 }" +# vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/worker-vhd${ count.index + 1 }.vhd" +# caching = "ReadWrite" +# create_option = "FromImage" +# } + +# os_profile { +# computer_name = "worker-node${ count.index + 1 }" +# admin_username = "${ var.admin-username }" +# admin_password = "Password1234!" +# custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" +# } + +# os_profile_linux_config { +# disable_password_authentication = true +# ssh_keys { +# path = "/home/${ var.admin-username }/.ssh/authorized_keys" +# key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" +# } +# } +# } + + From 540231fd17750061bd168134c8f30e7e69d09bd7 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 13 Apr 2017 06:12:25 +1200 Subject: [PATCH 082/149] Create Worker Nodes --- modules/worker/nodes.tf | 48 +++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/modules/worker/nodes.tf b/modules/worker/nodes.tf index 9fc4129..b243167 100644 --- a/modules/worker/nodes.tf +++ b/modules/worker/nodes.tf @@ -1,14 +1,40 @@ - - - - - - - - - - - +resource "google_compute_instance" "cncf" { + count = "${ var.worker-node-count }" + name = "${ var.name }${ count.index + 1 }" + machine_type = "n1-standard-1" + zone = "${ var.zone }" + + tags = ["foo", "bar"] + + disk { + image = "coreos-stable-1298-7-0-v20170401" + } + + // Local SSD disk + disk { + type = "local-ssd" + scratch = true + } + + network_interface { + # network = "${ var.name }" + subnetwork = "${ var.name }" + subnetwork_project = "${ var.project }" + + access_config { + // FIX ME Don't assign Public IP + // Ephemeral IP + } + } + + metadata { + user-data = "${ data.template_file.cloud-config.rendered }" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} # resource "azurerm_network_interface" "cncf" { # count = "${ var.worker-node-count }" From f4a665bed352562803927b5bc52dc1435e943c12 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 13 Apr 2017 06:41:38 +1200 Subject: [PATCH 083/149] Update Worker Nodes --- modules/etcd/nodes.tf | 2 +- modules/worker/.#input.tf | 1 + modules/worker/cloud-config.tf | 2 +- modules/worker/input.tf | 30 ++++++++++++++++-------------- modules/worker/nodes.tf | 2 +- 5 files changed, 20 insertions(+), 17 deletions(-) create mode 120000 modules/worker/.#input.tf diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index 2c60758..92e2bb1 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -1,6 +1,6 @@ resource "google_compute_instance" "cncf" { count = "${ var.master-node-count }" - name = "${ var.name }${ count.index + 1 }" + name = "${ var.name }-master${ count.index + 1 }" machine_type = "n1-standard-1" zone = "${ var.zone }" diff --git a/modules/worker/.#input.tf b/modules/worker/.#input.tf new file mode 120000 index 0000000..87e0512 --- /dev/null +++ b/modules/worker/.#input.tf @@ -0,0 +1 @@ +dlx@W541.20795:1491707970 \ No newline at end of file diff --git a/modules/worker/cloud-config.tf b/modules/worker/cloud-config.tf index 94b7e97..7f1256d 100644 --- a/modules/worker/cloud-config.tf +++ b/modules/worker/cloud-config.tf @@ -10,6 +10,6 @@ data "template_file" "cloud-config" { ca = "${ var.ca }" k8s-worker = "${ var.k8s-worker }" k8s-worker-key = "${ var.k8s-worker-key }" - cloud-config = "${ base64encode(var.cloud-config) }" + # cloud-config = "${ base64encode(var.cloud-config) }" } } diff --git a/modules/worker/input.tf b/modules/worker/input.tf index e02c5d1..a0af376 100644 --- a/modules/worker/input.tf +++ b/modules/worker/input.tf @@ -1,24 +1,26 @@ -variable "location" {} -variable "subnet-id" {} +variable "region" {} +variable "zone" {} +variable "project" {} +# variable "subnet-id" {} variable "name" {} -variable "worker-vm-size" {} +# variable "worker-vm-size" {} variable "worker-node-count" {} -variable "image-publisher" {} -variable "image-offer" {} -variable "image-sku" {} -variable "image-version" {} -variable "storage-account" {} -variable "storage-primary-endpoint" {} -variable "storage-container" {} -variable "availability-id" {} -variable "external-lb" {} +# variable "image-publisher" {} +# variable "image-offer" {} +# variable "image-sku" {} +# variable "image-version" {} +# variable "storage-account" {} +# variable "storage-primary-endpoint" {} +# variable "storage-container" {} +# variable "availability-id" {} +# variable "external-lb" {} variable "cluster-domain" {} variable "dns-service-ip" {} variable "internal-tld" {} -variable "admin-username" {} +# variable "admin-username" {} variable "kubelet-image-url" {} variable "kubelet-image-tag" {} variable "ca" {} variable "k8s-worker" {} variable "k8s-worker-key" {} -variable "cloud-config" {} +# variable "cloud-config" {} diff --git a/modules/worker/nodes.tf b/modules/worker/nodes.tf index b243167..3942ab8 100644 --- a/modules/worker/nodes.tf +++ b/modules/worker/nodes.tf @@ -1,6 +1,6 @@ resource "google_compute_instance" "cncf" { count = "${ var.worker-node-count }" - name = "${ var.name }${ count.index + 1 }" + name = "${ var.name }-worker${ count.index + 1 }" machine_type = "n1-standard-1" zone = "${ var.zone }" From 5362d607e88b13e0292271dfb0af64cefdb46a69 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 13 Apr 2017 09:25:53 +1200 Subject: [PATCH 084/149] Remove SSL Depends --- modules/etcd/cloud-config.yml | 20 ++------------------ modules/worker/cloud-config.yml | 11 +---------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index 196f04d..aabe095 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -23,12 +23,6 @@ coreos: units: - name: etcd2.service command: start - drop-ins: - - name: wait-for-certs.conf - content: | - [Unit] - After=get-ssl.service - Requires=get-ssl.service - name: docker.service command: start @@ -38,14 +32,6 @@ coreos: [Service] Environment="DOCKER_OPTS=--storage-driver=overlay" - - name: get-ssl.service - command: start - content: | - [Service] - ExecStart=/bin/sh -c "tar xv -f /etc/kubernetes/ssl/k8s-apiserver.tar -C /etc/kubernetes/ssl/" - RemainAfterExit=yes - Type=oneshot - - name: kubelet.service command: start content: | @@ -72,8 +58,7 @@ coreos: ExecStart=/usr/lib/coreos/kubelet-wrapper \ --allow-privileged=true \ --api-servers=http://127.0.0.1:8080 \ - --cloud-provider=azure \ - --cloud-config=/etc/kubernetes/ssl/azure-config.json \ + --cloud-provider=gce \ --cluster-dns=${ dns-service-ip } \ --cluster-domain=${ cluster-domain } \ --config=/etc/kubernetes/manifests \ @@ -117,8 +102,7 @@ write-files: - --admission-control=ServiceAccount - --allow-privileged=true - --client-ca-file=/etc/kubernetes/ssl/ca.pem - - --cloud-provider=azure - - --cloud-config=/etc/kubernetes/ssl/azure-config.json + - --cloud-provider=gce - --etcd-servers=http://etcd.${ internal-tld }:2379 - --insecure-bind-address=0.0.0.0 - --secure-port=443 diff --git a/modules/worker/cloud-config.yml b/modules/worker/cloud-config.yml index f54fdda..87644a6 100644 --- a/modules/worker/cloud-config.yml +++ b/modules/worker/cloud-config.yml @@ -18,14 +18,6 @@ coreos: - name: docker.service command: start - - name: get-ssl.service - command: start - content: | - [Service] - ExecStart=/bin/sh -c "tar xv -f /etc/kubernetes/ssl/k8s-worker.tar -C /etc/kubernetes/ssl/" - RemainAfterExit=yes - Type=oneshot - - name: kubelet.service command: start content: | @@ -52,8 +44,7 @@ coreos: ExecStart=/usr/lib/coreos/kubelet-wrapper \ --allow-privileged=true \ --api-servers=http://master.${ internal-tld }:8080 \ - --cloud-provider=azure \ - --cloud-config=/etc/kubernetes/ssl/azure-config.json \ + --cloud-provider=gce \ --cluster-dns=${ dns-service-ip } \ --cluster-domain=${ cluster-domain } \ --config=/etc/kubernetes/manifests \ From 8723bbbe7ca7636c4a4f8a5e4c8255029918585a Mon Sep 17 00:00:00 2001 From: DLX Date: Sun, 16 Apr 2017 12:10:50 +1200 Subject: [PATCH 085/149] Override resolv.conf --- input.tf | 1 + modules.tf | 2 ++ modules/dns/dns.tf | 24 ++++++++++++++++++++++++ modules/dns/input.tf | 1 + modules/etcd/cloud-config.tf | 1 + modules/etcd/cloud-config.yml | 11 ++++++++++- modules/etcd/input.tf | 1 + 7 files changed, 40 insertions(+), 1 deletion(-) diff --git a/input.tf b/input.tf index e8bda74..7178700 100644 --- a/input.tf +++ b/input.tf @@ -24,3 +24,4 @@ variable "k8s-service-ip" { default = "10.3.0.1" } variable "dns-service-ip" { default = "10.3.0.10" } variable "allow-ssh-cidr" { default = "0.0.0.0/0" } variable "data-dir" { default = "/cncf/data" } +variable "name-servers-file" { default = "google_dns_zone"} diff --git a/modules.tf b/modules.tf index 04460a1..ba85112 100644 --- a/modules.tf +++ b/modules.tf @@ -12,6 +12,7 @@ module "dns" { internal-tld = "${ var.internal-tld }" master-ips = "${ module.etcd.master-ips }" master-node-count = "${ var.master-node-count }" + name-servers-file = "${ var.name-servers-file }" } module "etcd" { @@ -20,6 +21,7 @@ module "dns" { region = "${ var.region }" zone = "${ var.zone }" project = "${ var.project }" + name-servers-file = "${ var.name-servers-file }" # admin-username = "${ var.admin-username }" master-node-count = "${ var.master-node-count }" # master-vm-size = "${ var.master-vm-size }" diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index 2fcc8d8..081cbcb 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -4,6 +4,30 @@ resource "google_dns_managed_zone" "cncf" { description = "${ var.name }" } +#Name Servers +resource "null_resource" "dns_dig" { + count = 2 + depends_on = [ "google_dns_managed_zone.cncf" ] + provisioner "local-exec" { + command = < ${ var.name-servers-file }.${ count.index }.ip +EOF + } +} + +resource "null_resource" "dns_gen" { + depends_on = [ "null_resource.dns_dig" ] + provisioner "local-exec" { + command = < ${ var.name-servers-file} +EOF + } +} + resource "google_dns_record_set" "A-etcd" { name = "${ var.name }.${ var.internal-tld }." type = "A" diff --git a/modules/dns/input.tf b/modules/dns/input.tf index 34adcac..c4eaf92 100644 --- a/modules/dns/input.tf +++ b/modules/dns/input.tf @@ -3,6 +3,7 @@ variable "internal-tld" {} variable "name" {} variable "master-ips" { type = "list" } variable "master-node-count" { default = "3" } +variable "name-servers-file" {} # variable "vpc-id" {} diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index 8765022..cc48180 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -19,6 +19,7 @@ data "template_file" "cloud-config" { k8s-etcd-key = "${ base64encode(var.k8s-etcd-key) }" k8s-apiserver = "${ base64encode(var.k8s-apiserver) }" k8s-apiserver-key = "${ base64encode(var.k8s-apiserver-key) }" + name-servers-file = "${ var.name-servers-file }" # cloud-config = "${ base64encode(var.cloud-config) }" } diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index aabe095..7bd1ec4 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -1,6 +1,16 @@ #cloud-config --- +# manage_resolv_conf: true + +# resolv_conf: +# nameservers: ['169.254.169.254'] +# searchdomains: +# - c.test-163823.internal +# options: +# rotate: true +# timeout: 1 + coreos: etcd2: @@ -273,4 +283,3 @@ write-files: - encoding: b64 content: "${ k8s-apiserver-key }" path: /etc/kubernetes/ssl/k8s-apiserver-key.pem - diff --git a/modules/etcd/input.tf b/modules/etcd/input.tf index 762dfd0..84c9fc6 100644 --- a/modules/etcd/input.tf +++ b/modules/etcd/input.tf @@ -27,6 +27,7 @@ variable "service-cidr" {} # variable "admin-username" {} variable "kubelet-image-url" {} variable "kubelet-image-tag" {} +variable "name-servers-file" {} # variable "cloud-config" {} # variable "etcd-security-group-id" {} From 36853927326e761dcb4482c22ddd86f3113c8b7c Mon Sep 17 00:00:00 2001 From: DLX Date: Tue, 18 Apr 2017 16:19:40 +1200 Subject: [PATCH 086/149] GCE Refactor --- cert.tf | 4 ++-- input.tf | 4 ++-- keypair.tf | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cert.tf b/cert.tf index 6271c2f..44182a1 100644 --- a/cert.tf +++ b/cert.tf @@ -4,7 +4,7 @@ resource "null_resource" "ssl_gen" { provisioner "local-exec" { command = < Date: Tue, 18 Apr 2017 16:21:00 +1200 Subject: [PATCH 087/149] Update ETCD to use and not DNS for Bootstrap --- modules.tf | 25 +++++++++--------- modules/dns/dns.tf | 48 +++++++++++++++++------------------ modules/dns/input.tf | 2 +- modules/etcd/cloud-config.tf | 3 ++- modules/etcd/cloud-config.yml | 22 ++++------------ modules/etcd/discovery.tf | 19 ++++++++++++++ modules/etcd/input.tf | 3 ++- modules/etcd/nodes.tf | 2 +- 8 files changed, 67 insertions(+), 57 deletions(-) create mode 100644 modules/etcd/discovery.tf diff --git a/modules.tf b/modules.tf index ba85112..b6be75f 100644 --- a/modules.tf +++ b/modules.tf @@ -11,7 +11,7 @@ module "dns" { name = "${ var.name }" internal-tld = "${ var.internal-tld }" master-ips = "${ module.etcd.master-ips }" - master-node-count = "${ var.master-node-count }" + master_node_count = "${ var.master_node_count }" name-servers-file = "${ var.name-servers-file }" } @@ -23,7 +23,8 @@ module "dns" { project = "${ var.project }" name-servers-file = "${ var.name-servers-file }" # admin-username = "${ var.admin-username }" - master-node-count = "${ var.master-node-count }" + master_node_count = "${ var.master_node_count }" + etcd_discovery = "${ var.data_dir }/etcd" # master-vm-size = "${ var.master-vm-size }" # image-publisher = "${ var.image-publisher }" # image-offer = "${ var.image-offer }" @@ -41,12 +42,12 @@ module "dns" { internal-tld = "${ var.internal-tld }" pod-cidr = "${ var.pod-cidr }" service-cidr = "${ var.service-cidr }" - k8s-apiserver-key = "${file("${ var.data-dir }/.cfssl/k8s-apiserver-key.pem")}" - k8s-apiserver = "${file("${ var.data-dir }/.cfssl/k8s-apiserver.pem")}" - k8s-etcd-key = "${file("${ var.data-dir }/.cfssl/k8s-etcd-key.pem")}" - k8s-etcd = "${file("${ var.data-dir }/.cfssl/k8s-etcd.pem")}" - ca = "${file("${ var.data-dir }/.cfssl/ca.pem")}" -# cloud-config = "${file("${ var.data-dir }/azure-config.json")}" + k8s-apiserver-key = "${file("${ var.data_dir }/.cfssl/k8s-apiserver-key.pem")}" + k8s-apiserver = "${file("${ var.data_dir }/.cfssl/k8s-apiserver.pem")}" + k8s-etcd-key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" + k8s-etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" +# cloud-config = "${file("${ var.data_dir }/azure-config.json")}" # # etcd-security-group-id = "${ module.security.etcd-id }" # # external-elb-security-group-id = "${ module.security.external-elb-id }" } @@ -95,10 +96,10 @@ module "worker" { kubelet-image-tag = "${ var.kubelet-image-tag }" dns-service-ip = "${ var.dns-service-ip }" internal-tld = "${ var.internal-tld }" - ca = "${file("${ var.data-dir }/.cfssl/ca.pem")}" - k8s-worker = "${file("${ var.data-dir }/.cfssl/k8s-worker.pem")}" - k8s-worker-key = "${file("${ var.data-dir }/.cfssl/k8s-worker-key.pem")}" - # cloud-config = "${file("${ var.data-dir }/azure-config.json")}" + ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s-worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" + k8s-worker-key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" + # cloud-config = "${file("${ var.data_dir }/azure-config.json")}" # security-group-id = "${ module.security.worker-id }" } diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index 081cbcb..ac638bf 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -4,29 +4,29 @@ resource "google_dns_managed_zone" "cncf" { description = "${ var.name }" } -#Name Servers -resource "null_resource" "dns_dig" { - count = 2 - depends_on = [ "google_dns_managed_zone.cncf" ] - provisioner "local-exec" { - command = < ${ var.name-servers-file }.${ count.index }.ip -EOF - } -} - -resource "null_resource" "dns_gen" { - depends_on = [ "null_resource.dns_dig" ] - provisioner "local-exec" { - command = < ${ var.name-servers-file} -EOF - } -} +# #Name Servers +# resource "null_resource" "dns_dig" { +# count = 2 +# depends_on = [ "google_dns_managed_zone.cncf" ] +# provisioner "local-exec" { +# command = < ${ var.name-servers-file }.${ count.index }.ip +# EOF +# } +# } + +# resource "null_resource" "dns_gen" { +# depends_on = [ "null_resource.dns_dig" ] +# provisioner "local-exec" { +# command = < ${ var.name-servers-file} +# EOF +# } +# } resource "google_dns_record_set" "A-etcd" { name = "${ var.name }.${ var.internal-tld }." @@ -41,7 +41,7 @@ resource "google_dns_record_set" "A-etcd" { } resource "google_dns_record_set" "A-etcds" { - count = "${ var.master-node-count }" + count = "${ var.master_node_count }" name = "${ var.name }${ count.index+1 }.${ var.internal-tld }." type = "A" ttl = 300 diff --git a/modules/dns/input.tf b/modules/dns/input.tf index c4eaf92..abb8220 100644 --- a/modules/dns/input.tf +++ b/modules/dns/input.tf @@ -2,7 +2,7 @@ variable "internal-tld" {} variable "name" {} variable "master-ips" { type = "list" } -variable "master-node-count" { default = "3" } +variable "master_node_count" {} variable "name-servers-file" {} diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index cc48180..d83c245 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -1,5 +1,5 @@ data "template_file" "cloud-config" { - count = "${ var.master-node-count }" + count = "${ var.master_node_count }" template = "${ file( "${ path.module }/cloud-config.yml" )}" vars { @@ -20,6 +20,7 @@ data "template_file" "cloud-config" { k8s-apiserver = "${ base64encode(var.k8s-apiserver) }" k8s-apiserver-key = "${ base64encode(var.k8s-apiserver-key) }" name-servers-file = "${ var.name-servers-file }" + etcd_discovery = "${ file(var.etcd_discovery) }" # cloud-config = "${ base64encode(var.cloud-config) }" } diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index 7bd1ec4..c81619e 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -1,30 +1,18 @@ #cloud-config --- -# manage_resolv_conf: true - -# resolv_conf: -# nameservers: ['169.254.169.254'] -# searchdomains: -# - c.test-163823.internal -# options: -# rotate: true -# timeout: 1 - coreos: etcd2: - advertise-client-urls: http://${ fqdn }:2379 + advertise-client-urls: http://$private_ipv4:2379 # cert-file: /etc/kubernetes/ssl/k8s-etcd.pem debug: true - discovery-srv: ${ internal-tld } - initial-advertise-peer-urls: https://${ fqdn }:2380 - initial-cluster-state: new - initial-cluster-token: ${ cluster-token } + initial-advertise-peer-urls: https://$private_ipv4:2380 # key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem - listen-client-urls: http://${ fqdn }:2379,http://127.0.0.1:2379 - listen-peer-urls: https://${ fqdn }:2380 + listen-client-urls: http://$private_ipv4:2379,http://127.0.0.1:2379 + listen-peer-urls: https://$private_ipv4:2380 name: ${ hostname } + discovery: ${ etcd_discovery } peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem peer-client-cert-auth: true peer-cert-file: /etc/kubernetes/ssl/k8s-etcd.pem diff --git a/modules/etcd/discovery.tf b/modules/etcd/discovery.tf new file mode 100644 index 0000000..65865ff --- /dev/null +++ b/modules/etcd/discovery.tf @@ -0,0 +1,19 @@ + +#Get Discovery URL +resource "null_resource" "discovery_gen" { + + provisioner "local-exec" { + command = < ${ var.etcd_discovery } +EOF + } + + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = < Date: Thu, 20 Apr 2017 15:25:22 +1200 Subject: [PATCH 088/149] Update Cert to Use Project DNS for ETCD --- cert.tf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cert.tf b/cert.tf index 44182a1..0f1a8ad 100644 --- a/cert.tf +++ b/cert.tf @@ -7,7 +7,9 @@ ${ path.module }/init-cfssl \ ${ var.data_dir }/.cfssl \ ${ var.region } \ ${ var.internal-tld } \ -${ var.k8s-service-ip } +${ var.k8s-service-ip } \ +${ var.internal_lb } \ +${ var.project } EOF } From 4d68bec8bc51144a9c823f67fead3c39a8d832fd Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:26:06 +1200 Subject: [PATCH 089/149] Create Certs using Project DNS --- init-cfssl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/init-cfssl b/init-cfssl index e880777..52d6c54 100755 --- a/init-cfssl +++ b/init-cfssl @@ -20,6 +20,12 @@ INTERNAL_TLD=$3 K8S_SERVICE_IP=$4 [ -z "$K8S_SERVICE_IP" ] && usage +INTERNAL_LB=$5 +[ -z "$INTERNAL_LB" ] && usage + +PROJECT=$6 +[ -z "$PROJECT" ] && usage + set -o nounset set -o pipefail @@ -100,10 +106,10 @@ _chmod ca # generate keys and certs generate k8s-admin client-server "${DEFAULT_HOSTS}" -generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},master.${INTERNAL_TLD},*.${REGION}.cloudapp.azure.com" -generate k8s-etcd client-server "etcd.${INTERNAL_TLD},etcd1.${INTERNAL_TLD},etcd2.${INTERNAL_TLD},etcd3.${INTERNAL_TLD}" +generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},${INTERNAL_LB}" +generate k8s-etcd client-server "*.c.${PROJECT}.internal,test-master1.c.${PROJECT}.internal,test-master2.c.${PROJECT}.internal,test-master3.c.${PROJECT}.internal" generate k8s-worker client "${DEFAULT_HOSTS}" -# TODO: fix cert provisioning hacks +# Fix cert provisioning hacks tar -rf k8s-apiserver.tar k8s-etcd.pem k8s-etcd-key.pem tar -rf k8s-worker.tar ca.pem From 5c2ac6338dff83785f2ee462d504855d23137387 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:26:46 +1200 Subject: [PATCH 090/149] Create Internal LB for Masters --- input.tf | 1 + modules.tf | 39 +++++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/input.tf b/input.tf index 6a4380c..7bc4982 100644 --- a/input.tf +++ b/input.tf @@ -25,3 +25,4 @@ variable "dns-service-ip" { default = "10.3.0.10" } variable "allow-ssh-cidr" { default = "0.0.0.0/0" } variable "data_dir" { default = "/cncf/data" } variable "name-servers-file" { default = "google_dns_zone"} +variable "internal_lb" { default = "10.0.0.100" } diff --git a/modules.tf b/modules.tf index b6be75f..3934af6 100644 --- a/modules.tf +++ b/modules.tf @@ -5,15 +5,14 @@ module "vpc" { # name-servers-file = "${ module.dns.name-servers-file }" region = "${ var.region }" } - -module "dns" { - source = "./modules/dns" - name = "${ var.name }" - internal-tld = "${ var.internal-tld }" - master-ips = "${ module.etcd.master-ips }" - master_node_count = "${ var.master_node_count }" - name-servers-file = "${ var.name-servers-file }" -} +# module "dns" { +# source = "./modules/dns" +# name = "${ var.name }" +# internal-tld = "${ var.internal-tld }" +# # master-ips = "${ module.etcd.master-ips }" +# master_node_count = "${ var.master_node_count }" +# name-servers-file = "${ var.name-servers-file }" +# } module "etcd" { source = "./modules/etcd" @@ -21,6 +20,9 @@ module "dns" { region = "${ var.region }" zone = "${ var.zone }" project = "${ var.project }" + network = "${ module.vpc.network }" + subnetwork = "${ module.vpc.subnetwork }" + internal_lb = "${ var.internal_lb }" name-servers-file = "${ var.name-servers-file }" # admin-username = "${ var.admin-username }" master_node_count = "${ var.master_node_count }" @@ -78,6 +80,7 @@ module "worker" { region = "${ var.region }" zone = "${ var.zone }" project = "${ var.project }" + internal_lb = "${ var.internal_lb }" # admin-username = "${ var.admin-username }" worker-node-count = "${ var.worker-node-count }" # worker-vm-size = "${ var.worker-vm-size }" @@ -115,13 +118,13 @@ module "worker" { # } -# /* -# module "azuresecurity" { -# source = "./modules/security" -# cidr-allow-ssh = "${ var.cidr["allow-ssh"] }" -# cidr-vpc = "${ var.cidr["vpc"] }" -# name = "${ var.name }" -# vpc-id = "${ module.vpc.id }" -# } -# */ +module "security" { + source = "./modules/security" + + network = "${ module.vpc.network }" + # cidr-allow-ssh = "${ var.cidr["allow-ssh"] }" + # cidr-vpc = "${ var.cidr["vpc"] }" + name = "${ var.name }" + # vpc-id = "${ module.vpc.id }" +} From af75be866c67cce07b11834f0b24d88ba64c23f5 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:27:44 +1200 Subject: [PATCH 091/149] Add LB and Bastion Node Firewall Rules --- modules/bastion/node.tf | 2 +- modules/security/io.tf | 15 ++-- modules/security/security.tf | 131 +++++++++-------------------------- 3 files changed, 41 insertions(+), 107 deletions(-) diff --git a/modules/bastion/node.tf b/modules/bastion/node.tf index 2424465..ed25609 100644 --- a/modules/bastion/node.tf +++ b/modules/bastion/node.tf @@ -3,7 +3,7 @@ resource "google_compute_instance" "cncf" { machine_type = "n1-standard-1" zone = "${ var.zone }" - tags = ["foo", "bar"] + tags = ["bastion", "bar"] disk { image = "coreos-stable-1298-7-0-v20170401" diff --git a/modules/security/io.tf b/modules/security/io.tf index 03ca9a0..4fe78e6 100644 --- a/modules/security/io.tf +++ b/modules/security/io.tf @@ -1,9 +1,10 @@ -variable "cidr-allow-ssh" {} -variable "cidr-vpc" {} +# variable "cidr-allow-ssh" {} +# variable "cidr-vpc" {} variable "name" {} -variable "vpc-id" {} +# variable "vpc-id" {} +variable "network" {} -output "bastion-id" { value = "${ aws_security_group.bastion.id }" } -output "etcd-id" { value = "${ aws_security_group.etcd.id }" } -output "external-elb-id" { value = "${ aws_security_group.external-elb.id }" } -output "worker-id" { value = "${ aws_security_group.worker.id }" } +# output "bastion-id" { value = "${ aws_security_group.bastion.id }" } +# output "etcd-id" { value = "${ aws_security_group.etcd.id }" } +# output "external-elb-id" { value = "${ aws_security_group.external-elb.id }" } +# output "worker-id" { value = "${ aws_security_group.worker.id }" } diff --git a/modules/security/security.tf b/modules/security/security.tf index 70f2d68..d62e6f5 100644 --- a/modules/security/security.tf +++ b/modules/security/security.tf @@ -1,123 +1,56 @@ -resource "aws_security_group" "bastion" { - description = "k8s bastion security group" +resource "google_compute_firewall" "allow-internal-lb" { + name = "allow-internal-lb" + network = "${ var.name }" - egress = { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress = { - from_port = 22 - to_port = 22 + allow { protocol = "tcp" - cidr_blocks = [ "${ var.cidr-allow-ssh }" ] - } - - name = "bastion-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "bastion-k8s-${ var.name }" - builtWith = "terraform" + ports = ["8080", "443"] } - vpc_id = "${ var.vpc-id }" + source_ranges = ["10.0.0.0/16"] + target_tags = ["int-lb"] } -resource "aws_security_group" "etcd" { - description = "k8s etcd security group" - - egress = { - from_port = 0 - to_port = 0 - protocol = "-1" - /*self = true*/ - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress = { - from_port = 0 - to_port = 0 - protocol = "-1" - self = true - cidr_blocks = [ "${ var.cidr-vpc }" ] - } +resource "google_compute_firewall" "allow-health-check" { + name = "allow-health-check" + network = "${ var.name }" - name = "etcd-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "etcd-k8s-${ var.name }" - builtWith = "terraform" + allow { + protocol = "tcp" + ports = ["8080"] } - vpc_id = "${ var.vpc-id }" + source_ranges = ["130.211.0.0/22","35.191.0.0/16","10.0.0.0/16"] } -resource "aws_security_group" "external-elb" { - description = "k8s-${ var.name } master (apiserver) external elb" +resource "google_compute_firewall" "allow-all-internal" { + name = "allow-all-10-128-0-0-20" + network = "${ var.name }" - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - /*cidr_blocks = [ "${ var.cidr-vpc }" ]*/ - security_groups = [ "${ aws_security_group.etcd.id }" ] - } - - ingress { - from_port = -1 - to_port = -1 - protocol = "icmp" - cidr_blocks = [ "0.0.0.0/0" ] + allow { + protocol = "tcp" } - ingress { - from_port = 443 - to_port = 443 - protocol = "tcp" - cidr_blocks = [ "0.0.0.0/0" ] + allow { + protocol = "udp" } - name = "master-external-elb-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "master-external-elb-k8s-${ var.name }" - builtWith = "terraform" + allow { + protocol = "icmp" } - vpc_id = "${ var.vpc-id }" + source_ranges = ["10.0.0.0/16"] } -resource "aws_security_group" "worker" { - description = "k8s worker security group" +resource "google_compute_firewall" "allow-ssh-bastion" { + name = "allow-ssh-bastion" + network = "${ var.name }" - egress = { - from_port = 0 - to_port = 0 - protocol = "-1" - /*self = true*/ - cidr_blocks = [ "0.0.0.0/0" ] - } - - ingress = { - from_port = 0 - to_port = 0 - protocol = "-1" - self = true - cidr_blocks = [ "${ var.cidr-vpc }" ] - } - - name = "worker-k8s-${ var.name }" - - tags { - KubernetesCluster = "${ var.name }" - Name = "worker-k8s-${ var.name }" - builtWith = "terraform" + allow { + protocol = "tcp" + ports = ["22"] } - vpc_id = "${ var.vpc-id }" + source_ranges = ["0.0.0.0/0"] + target_tags = ["bastion"] } From 96a4b1049e18e5da403e27b23752c783058b8717 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:28:31 +1200 Subject: [PATCH 092/149] Bootstrap Cluster Using Project DNS --- modules/etcd/cloud-config.tf | 4 ++-- modules/etcd/cloud-config.yml | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/etcd/cloud-config.tf b/modules/etcd/cloud-config.tf index d83c245..fba5ae1 100644 --- a/modules/etcd/cloud-config.tf +++ b/modules/etcd/cloud-config.tf @@ -7,8 +7,8 @@ data "template_file" "cloud-config" { cluster-token = "etcd-cluster-${ var.name }" dns-service-ip = "${ var.dns-service-ip }" etc-tar = "/manifests/etc.tar" - fqdn = "etcd${ count.index + 1 }.${ var.internal-tld }" - hostname = "etcd${ count.index + 1 }" + fqdn = "${ var.name}-master${ count.index + 1 }.c.${ var.project }.internal" + hostname = "${ var.name }-master${ count.index + 1 }.c.${ var.project }.internal" kubelet-image-url = "${ var.kubelet-image-url }" kubelet-image-tag = "${ var.kubelet-image-tag }" internal-tld = "${ var.internal-tld }" diff --git a/modules/etcd/cloud-config.yml b/modules/etcd/cloud-config.yml index c81619e..d874db0 100644 --- a/modules/etcd/cloud-config.yml +++ b/modules/etcd/cloud-config.yml @@ -4,14 +4,15 @@ coreos: etcd2: - advertise-client-urls: http://$private_ipv4:2379 + advertise-client-urls: http://${ fqdn }:2379 # cert-file: /etc/kubernetes/ssl/k8s-etcd.pem debug: true - initial-advertise-peer-urls: https://$private_ipv4:2380 + initial-advertise-peer-urls: https://${ fqdn }:2380 # key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem - listen-client-urls: http://$private_ipv4:2379,http://127.0.0.1:2379 - listen-peer-urls: https://$private_ipv4:2380 - name: ${ hostname } + listen-client-urls: http://${ fqdn }:2379,http://127.0.0.1:2379 + listen-peer-urls: https://${ fqdn }:2380 + advertise-client-urls: http://${ fqdn }:2379 + name: ${ hostname } discovery: ${ etcd_discovery } peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem peer-client-cert-auth: true @@ -101,7 +102,7 @@ write-files: - --allow-privileged=true - --client-ca-file=/etc/kubernetes/ssl/ca.pem - --cloud-provider=gce - - --etcd-servers=http://etcd.${ internal-tld }:2379 + - --etcd-servers=http://${ fqdn }:2379 - --insecure-bind-address=0.0.0.0 - --secure-port=443 - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem @@ -153,8 +154,7 @@ write-files: command: - /hyperkube - controller-manager - - --cloud-provider=azure - - --cloud-config=/etc/kubernetes/ssl/azure-config.json + - --cloud-provider=gce - --leader-elect=true - --master=http://127.0.0.1:8080 - --root-ca-file=/etc/kubernetes/ssl/ca.pem From 77ff613239dddd243a01dbcc378cda78d88a1850 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:29:29 +1200 Subject: [PATCH 093/149] Update Master VARS --- modules/etcd/input.tf | 3 +++ modules/etcd/output.tf | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/etcd/input.tf b/modules/etcd/input.tf index 0044377..3ee6405 100644 --- a/modules/etcd/input.tf +++ b/modules/etcd/input.tf @@ -1,6 +1,7 @@ # variable "location" {} # variable "subnet-id" {} variable "name" {} +variable "internal_lb" {} variable "region" {} variable "zone" {} variable "project" {} @@ -29,6 +30,8 @@ variable "kubelet-image-url" {} variable "kubelet-image-tag" {} variable "name-servers-file" {} variable "etcd_discovery" {} +variable "network" {} +variable "subnetwork" {} # variable "cloud-config" {} # variable "etcd-security-group-id" {} diff --git a/modules/etcd/output.tf b/modules/etcd/output.tf index ee3e32d..f0bffd1 100644 --- a/modules/etcd/output.tf +++ b/modules/etcd/output.tf @@ -5,4 +5,4 @@ # # output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } # # output "external-elb" { value = "${ aws_elb.external.dns_name }" } # # output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } -output "master-ips" { value = ["${ google_compute_instance.cncf.*.network_interface.0.address }"] } +# output "master-ips" { value = ["${ google_compute_instance.cncf.*.network_interface.0.address }"] } From f3aa4ab52918962226c996a8f797aef5c88e3982 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:30:04 +1200 Subject: [PATCH 094/149] ETCD Bootstrap --- modules/etcd/nodes.tf | 88 ++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 52 deletions(-) diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index 3ed13cf..2e206bc 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -1,3 +1,27 @@ +resource "google_compute_region_backend_service" "cncf" { + name = "${ var.name }" + region = "${ var.region }" + protocol = "TCP" + timeout_sec = 10 + session_affinity = "CLIENT_IP" + + backend { + group = "${google_compute_instance_group.cncf.self_link}" + } + + health_checks = ["${google_compute_health_check.cncf.self_link}"] +} + +resource "google_compute_instance_group" "cncf" { + name = "${ var.name }" + instances = ["${google_compute_instance.cncf.*.self_link}"] + named_port = { + name = "http" + port = "8080" + } + zone = "${ var.zone }" +} + resource "google_compute_instance" "cncf" { count = "${ var.master_node_count }" name = "${ var.name }-master${ count.index + 1 }" @@ -36,56 +60,16 @@ resource "google_compute_instance" "cncf" { } } -# resource "azurerm_network_interface" "cncf" { -# count = "${ var.master-node-count }" -# name = "etcd-interface${ count.index + 1 }" -# location = "${ var.location }" -# resource_group_name = "${ var.name }" +resource "google_compute_health_check" "cncf" { + name = "${ var.name }" + check_interval_sec = 30 + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout_sec = 3 -# ip_configuration { -# name = "etcd-nic${ count.index + 1 }" -# subnet_id = "${ var.subnet-id }" -# private_ip_address_allocation = "dynamic" -# # private_ip_address = "${ element( split(",", var.etcd-ips), count.index ) }" -# load_balancer_backend_address_pools_ids = ["${ azurerm_lb_backend_address_pool.cncf.id }"] -# } -# } - -# resource "azurerm_virtual_machine" "cncf" { -# count = "${ var.master-node-count }" -# name = "etcd-master${ count.index + 1 }" -# location = "${ var.location }" -# availability_set_id = "${ var.availability-id }" -# resource_group_name = "${ var.name }" -# network_interface_ids = ["${ element(azurerm_network_interface.cncf.*.id, count.index) }"] -# vm_size = "${ var.master-vm-size }" - -# storage_image_reference { -# publisher = "${ var.image-publisher }" -# offer = "${ var.image-offer }" -# sku = "${ var.image-sku }" -# version = "${ var.image-version}" -# } - -# storage_os_disk { -# name = "etcd-disks${ count.index + 1 }" -# vhd_uri = "${ var.storage-primary-endpoint }${ var.storage-container }/etcd-vhd${ count.index + 1 }.vhd" -# caching = "ReadWrite" -# create_option = "FromImage" -# } - -# os_profile { -# computer_name = "etcd-master${ count.index + 1 }" -# admin_username = "${ var.admin-username }" -# admin_password = "Password1234!" -# custom_data = "${ element(data.template_file.cloud-config.*.rendered, count.index) }" -# } - -# os_profile_linux_config { -# disable_password_authentication = true -# ssh_keys { -# path = "/home/${ var.admin-username }/.ssh/authorized_keys" -# key_data = "${file("/cncf/data/.ssh/id_rsa.pub")}" -# } -# } -# } + http_health_check { + port = "8080" + host = "" + request_path = "/" + } +} From d7e9214a90fca7ae88a3f3b789a3e4fd49d45a7d Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:31:06 +1200 Subject: [PATCH 095/149] Configure Worker to Use Internal_LB for K8s Master --- modules/worker/.#input.tf | 1 - modules/worker/cloud-config.tf | 1 + modules/worker/cloud-config.yml | 2 +- modules/worker/input.tf | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 120000 modules/worker/.#input.tf diff --git a/modules/worker/.#input.tf b/modules/worker/.#input.tf deleted file mode 120000 index 87e0512..0000000 --- a/modules/worker/.#input.tf +++ /dev/null @@ -1 +0,0 @@ -dlx@W541.20795:1491707970 \ No newline at end of file diff --git a/modules/worker/cloud-config.tf b/modules/worker/cloud-config.tf index 7f1256d..ac74294 100644 --- a/modules/worker/cloud-config.tf +++ b/modules/worker/cloud-config.tf @@ -10,6 +10,7 @@ data "template_file" "cloud-config" { ca = "${ var.ca }" k8s-worker = "${ var.k8s-worker }" k8s-worker-key = "${ var.k8s-worker-key }" + internal_lb = "${ var.internal_lb }" # cloud-config = "${ base64encode(var.cloud-config) }" } } diff --git a/modules/worker/cloud-config.yml b/modules/worker/cloud-config.yml index 87644a6..5e1c82b 100644 --- a/modules/worker/cloud-config.yml +++ b/modules/worker/cloud-config.yml @@ -43,7 +43,7 @@ coreos: ExecStartPre=/usr/bin/mount --make-shared /var/lib/kubelet ExecStart=/usr/lib/coreos/kubelet-wrapper \ --allow-privileged=true \ - --api-servers=http://master.${ internal-tld }:8080 \ + --api-servers=http://${ internal_lb }:8080 \ --cloud-provider=gce \ --cluster-dns=${ dns-service-ip } \ --cluster-domain=${ cluster-domain } \ diff --git a/modules/worker/input.tf b/modules/worker/input.tf index a0af376..122d605 100644 --- a/modules/worker/input.tf +++ b/modules/worker/input.tf @@ -5,6 +5,7 @@ variable "project" {} variable "name" {} # variable "worker-vm-size" {} variable "worker-node-count" {} +variable "internal_lb" {} # variable "image-publisher" {} # variable "image-offer" {} # variable "image-sku" {} From 641f9fa52780b8f562847f15446c05501f901396 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:31:49 +1200 Subject: [PATCH 096/149] Add Internal_lb for K8s Masters --- modules/etcd/.#cloud-config.yml | 1 + modules/etcd/internal_lb.tf | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 120000 modules/etcd/.#cloud-config.yml create mode 100644 modules/etcd/internal_lb.tf diff --git a/modules/etcd/.#cloud-config.yml b/modules/etcd/.#cloud-config.yml new file mode 120000 index 0000000..546f459 --- /dev/null +++ b/modules/etcd/.#cloud-config.yml @@ -0,0 +1 @@ +dlx@W541.4550:1491707970 \ No newline at end of file diff --git a/modules/etcd/internal_lb.tf b/modules/etcd/internal_lb.tf new file mode 100644 index 0000000..7c756a7 --- /dev/null +++ b/modules/etcd/internal_lb.tf @@ -0,0 +1,10 @@ +resource "google_compute_forwarding_rule" "default" { + name = "${ var.name }" + load_balancing_scheme = "INTERNAL" + ip_address = "${ var.internal_lb }" + region = "${ var.region }" + ports = ["8080"] + network = "${ var.network }" + subnetwork = "${ var.subnetwork }" + backend_service = "${ google_compute_region_backend_service.cncf.self_link }" +} From d1afc247408870822a05bc8bd36c3ca9f9ae8b0a Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 15:32:38 +1200 Subject: [PATCH 097/149] Output Network Self_link --- modules/vpc/output.tf | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 modules/vpc/output.tf diff --git a/modules/vpc/output.tf b/modules/vpc/output.tf new file mode 100644 index 0000000..f6b4534 --- /dev/null +++ b/modules/vpc/output.tf @@ -0,0 +1,2 @@ +output "network" { value = "${ google_compute_network.cncf.self_link }" } +output "subnetwork" { value = "${ google_compute_subnetwork.cncf.self_link }" } From 2f636bfd42304d7a63265c84e34198496b072f60 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 16:56:11 +1200 Subject: [PATCH 098/149] Add External LB for K8s Endpoint --- modules.tf | 16 ++++++++-------- modules/etcd/external_lb.tf | 6 ++++++ modules/etcd/nodes.tf | 11 +++++++++++ modules/etcd/output.tf | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 modules/etcd/external_lb.tf diff --git a/modules.tf b/modules.tf index 3934af6..fab8f1e 100644 --- a/modules.tf +++ b/modules.tf @@ -107,15 +107,15 @@ module "worker" { } -# module "kubeconfig" { -# source = "./modules/kubeconfig" +module "kubeconfig" { + source = "./modules/kubeconfig" -# admin-key-pem = "${ var.data-dir }/.cfssl/k8s-admin-key.pem" -# admin-pem = "${ var.data-dir }/.cfssl/k8s-admin.pem" -# ca-pem = "${ var.data-dir }/.cfssl/ca.pem" -# fqdn-k8s = "${ module.etcd.fqdn-lb }" -# name = "${ var.name }" -# } + admin-key-pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" + admin-pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" + ca-pem = "${ var.data_dir }/.cfssl/ca.pem" + external-lb = "${ module.etcd.external-lb }" + name = "${ var.name }" +} diff --git a/modules/etcd/external_lb.tf b/modules/etcd/external_lb.tf new file mode 100644 index 0000000..d2b8906 --- /dev/null +++ b/modules/etcd/external_lb.tf @@ -0,0 +1,6 @@ +resource "google_compute_forwarding_rule" "external" { + name = "external${ var.name }" + target = "${google_compute_target_pool.cncf.self_link}" + port_range = "443" + load_balancing_scheme = "EXTERNAL" +} diff --git a/modules/etcd/nodes.tf b/modules/etcd/nodes.tf index 2e206bc..a27be48 100644 --- a/modules/etcd/nodes.tf +++ b/modules/etcd/nodes.tf @@ -12,6 +12,16 @@ resource "google_compute_region_backend_service" "cncf" { health_checks = ["${google_compute_health_check.cncf.self_link}"] } +resource "google_compute_target_pool" "cncf" { + name = "${var.name}-external" + + instances = [ + "${google_compute_instance.cncf.*.self_link}" + ] + + # health_checks = ["${google_compute_health_check.cncf.self_link}"] +} + resource "google_compute_instance_group" "cncf" { name = "${ var.name }" instances = ["${google_compute_instance.cncf.*.self_link}"] @@ -73,3 +83,4 @@ resource "google_compute_health_check" "cncf" { request_path = "/" } } + diff --git a/modules/etcd/output.tf b/modules/etcd/output.tf index f0bffd1..f866df5 100644 --- a/modules/etcd/output.tf +++ b/modules/etcd/output.tf @@ -1,4 +1,4 @@ -# output "external-lb" { value = "${azurerm_lb_backend_address_pool.cncf.id }" } +output "external-lb" { value = "${google_compute_forwarding_rule.external.ip_address }" } # output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } From 38e2dd20ffc3abffd3081ffe1de04688e52db5cc Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 16:56:54 +1200 Subject: [PATCH 099/149] Add Security Group for K8s Endpoint --- modules/security/security.tf | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/security/security.tf b/modules/security/security.tf index d62e6f5..2ab6609 100644 --- a/modules/security/security.tf +++ b/modules/security/security.tf @@ -54,3 +54,16 @@ resource "google_compute_firewall" "allow-ssh-bastion" { source_ranges = ["0.0.0.0/0"] target_tags = ["bastion"] } + +resource "google_compute_firewall" "allow-kubectl" { + name = "allow-kubectl-lb" + network = "${ var.name }" + + allow { + protocol = "tcp" + ports = ["443"] + } + + source_ranges = ["0.0.0.0/0"] + target_tags = ["foo"] +} From 2a069db283fef0f28747316c8b33cc75f8eef9b3 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 16:57:43 +1200 Subject: [PATCH 100/149] Create ENV File for KubectlA --- modules/kubeconfig/io.tf | 4 ++-- modules/kubeconfig/kubeconfig.tf | 33 +------------------------------- output.tf | 2 +- 3 files changed, 4 insertions(+), 35 deletions(-) diff --git a/modules/kubeconfig/io.tf b/modules/kubeconfig/io.tf index fda7916..3f61507 100644 --- a/modules/kubeconfig/io.tf +++ b/modules/kubeconfig/io.tf @@ -1,8 +1,8 @@ variable "admin-key-pem" {} variable "admin-pem" {} variable "ca-pem" {} -variable "fqdn-k8s" {} +variable "external-lb" {} variable "name" {} -output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } +# output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/modules/kubeconfig/kubeconfig.tf b/modules/kubeconfig/kubeconfig.tf index 90c3a7b..1b07805 100644 --- a/modules/kubeconfig/kubeconfig.tf +++ b/modules/kubeconfig/kubeconfig.tf @@ -1,40 +1,9 @@ -data "template_file" "kubeconfig" { - template = < ./tmp/kubeconfig -${data.template_file.kubeconfig.rendered} -__USERDATA__ -LOCAL_EXEC - } - provisioner "local-exec" { command = < Date: Thu, 20 Apr 2017 19:14:15 +1200 Subject: [PATCH 101/149] Genterate Cert for Public Endpoint --- cert.tf | 4 +++- gce.tf | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cert.tf b/cert.tf index 0f1a8ad..ea5c207 100644 --- a/cert.tf +++ b/cert.tf @@ -9,7 +9,9 @@ ${ var.region } \ ${ var.internal-tld } \ ${ var.k8s-service-ip } \ ${ var.internal_lb } \ -${ var.project } +${ var.project } \ +${ var.domain } \ +${ var.name } EOF } diff --git a/gce.tf b/gce.tf index c4e53a9..ec80299 100644 --- a/gce.tf +++ b/gce.tf @@ -5,6 +5,9 @@ provider "google" { region = "${ var.region }" } +provider "dnsimple" { +} + # resource "google_project" "company-env" { # project_id = "${var.ENVIRONMENT}" # org_id = "${var.GCP_ORG_ID}" From 9911d139ad5bcfe537e3959829f4159ac8e50c2c Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 19:15:03 +1200 Subject: [PATCH 102/149] Fix Cert for PublicLB --- init-cfssl | 10 +++++++++- input.tf | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/init-cfssl b/init-cfssl index 52d6c54..e71bbf0 100755 --- a/init-cfssl +++ b/init-cfssl @@ -26,6 +26,14 @@ INTERNAL_LB=$5 PROJECT=$6 [ -z "$PROJECT" ] && usage +DOMAIN=$7 +[ -z "$DOMAIN" ] && usage + +NAME=$8 +[ -z "$NAME" ] && usage + + + set -o nounset set -o pipefail @@ -106,7 +114,7 @@ _chmod ca # generate keys and certs generate k8s-admin client-server "${DEFAULT_HOSTS}" -generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},${INTERNAL_LB}" +generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},${INTERNAL_LB},endpoint.${NAME}.${DOMAIN}" generate k8s-etcd client-server "*.c.${PROJECT}.internal,test-master1.c.${PROJECT}.internal,test-master2.c.${PROJECT}.internal,test-master3.c.${PROJECT}.internal" generate k8s-worker client "${DEFAULT_HOSTS}" diff --git a/input.tf b/input.tf index 7bc4982..ee620c6 100644 --- a/input.tf +++ b/input.tf @@ -26,3 +26,4 @@ variable "allow-ssh-cidr" { default = "0.0.0.0/0" } variable "data_dir" { default = "/cncf/data" } variable "name-servers-file" { default = "google_dns_zone"} variable "internal_lb" { default = "10.0.0.100" } +variable "domain" { default = "cncf.ci" } From e75938c06bb14693a1335d9312af62b81f283104 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 20 Apr 2017 19:15:39 +1200 Subject: [PATCH 103/149] Create Public DNS Name for K8s Endpoint --- modules/dns/dns.tf | 100 ++++--------------------------- modules/dns/input.tf | 10 +--- modules/dns/output.tf | 4 -- modules/kubeconfig/io.tf | 2 +- modules/kubeconfig/kubeconfig.tf | 2 +- modules/worker/cloud-config.tf | 6 +- 6 files changed, 20 insertions(+), 104 deletions(-) diff --git a/modules/dns/dns.tf b/modules/dns/dns.tf index ac638bf..8508e3f 100644 --- a/modules/dns/dns.tf +++ b/modules/dns/dns.tf @@ -1,90 +1,14 @@ -resource "google_dns_managed_zone" "cncf" { - name = "${ var.name }" - dns_name = "${ var.internal-tld }." - description = "${ var.name }" -} - -# #Name Servers -# resource "null_resource" "dns_dig" { -# count = 2 -# depends_on = [ "google_dns_managed_zone.cncf" ] -# provisioner "local-exec" { -# command = < ${ var.name-servers-file }.${ count.index }.ip -# EOF -# } -# } - -# resource "null_resource" "dns_gen" { -# depends_on = [ "null_resource.dns_dig" ] -# provisioner "local-exec" { -# command = < ${ var.name-servers-file} -# EOF -# } -# } - -resource "google_dns_record_set" "A-etcd" { - name = "${ var.name }.${ var.internal-tld }." - type = "A" - ttl = 300 - - managed_zone = "${ google_dns_managed_zone.cncf.name }" - - rrdatas = [ - "${ var.master-ips }" - ] -} - -resource "google_dns_record_set" "A-etcds" { - count = "${ var.master_node_count }" - name = "${ var.name }${ count.index+1 }.${ var.internal-tld }." +# domain - (Required) The domain to add the record to +# name - (Required) The name of the record +# value - (Required) The value of the record +# type - (Required) The type of the record +# ttl - (Optional) The TTL of the record +# priority - (Optional) The priority of the record - only useful for some record typesi + +resource "dnsimple_record" "A-public-endpoint" { + name = "endpoint.${ var.name }" + value = "${ var.external_lb}" type = "A" - ttl = 300 - - managed_zone = "${ google_dns_managed_zone.cncf.name }" - - rrdatas = [ - "${ element(var.master-ips, count.index) }" - ] -} - -resource "google_dns_record_set" "CNAME-master" { - name = "master_${ var.name }.${ var.internal-tld }." - type = "CNAME" - ttl = 300 - - managed_zone = "${ google_dns_managed_zone.cncf.name }" - - rrdatas = [ - "${ var.name }.${ var.internal-tld }." - ] -} - -resource "google_dns_record_set" "etcd-client-tcp" { - name = "_etcd-client._tcp.${ var.internal-tld }." - type = "SRV" - ttl = 300 - - managed_zone = "${ google_dns_managed_zone.cncf.name }" - - rrdatas = [ - "${ formatlist("0 0 2379 %v", google_dns_record_set.A-etcds.*.name) }" - ] -} - -resource "google_dns_record_set" "etcd-server-tcp" { - name = "_etcd-server-ssl._tcp.${ var.internal-tld }." - type = "SRV" - ttl = 300 - - managed_zone = "${ google_dns_managed_zone.cncf.name }" - - rrdatas = [ - "${ formatlist("0 0 2380 %v", google_dns_record_set.A-etcds.*.name) }" - ] + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" } diff --git a/modules/dns/input.tf b/modules/dns/input.tf index abb8220..c5bc7f5 100644 --- a/modules/dns/input.tf +++ b/modules/dns/input.tf @@ -1,9 +1,5 @@ -# variable "depends-id" {} -variable "internal-tld" {} variable "name" {} -variable "master-ips" { type = "list" } variable "master_node_count" {} -variable "name-servers-file" {} - - -# variable "vpc-id" {} +variable "external_lb" {} +variable "domain" {} +variable "record_ttl" { default = "60" } diff --git a/modules/dns/output.tf b/modules/dns/output.tf index bd8187e..e69de29 100644 --- a/modules/dns/output.tf +++ b/modules/dns/output.tf @@ -1,4 +0,0 @@ -# output "depends-id" { value = "${null_resource.dummy_dependency.id}" } -# output "internal-name-servers" { value = "${ azurerm_dns_zone.cncf.name_servers }" } -# output "internal-zone-id" { value = "${ azurerm_dns_zone.cncf.zone_id }" } -# output "name-servers-file" { value = "${ var.name-servers-file }" } diff --git a/modules/kubeconfig/io.tf b/modules/kubeconfig/io.tf index 3f61507..538875a 100644 --- a/modules/kubeconfig/io.tf +++ b/modules/kubeconfig/io.tf @@ -1,7 +1,7 @@ variable "admin-key-pem" {} variable "admin-pem" {} variable "ca-pem" {} -variable "external-lb" {} +variable "external_fqdn" {} variable "name" {} diff --git a/modules/kubeconfig/kubeconfig.tf b/modules/kubeconfig/kubeconfig.tf index 1b07805..8052878 100644 --- a/modules/kubeconfig/kubeconfig.tf +++ b/modules/kubeconfig/kubeconfig.tf @@ -3,7 +3,7 @@ resource "null_resource" "kubeconfig" { provisioner "local-exec" { command = < Date: Thu, 20 Apr 2017 19:16:36 +1200 Subject: [PATCH 104/149] Update Outputs --- modules.tf | 10 +++++++++- output.tf | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules.tf b/modules.tf index fab8f1e..527f398 100644 --- a/modules.tf +++ b/modules.tf @@ -14,6 +14,14 @@ module "vpc" { # name-servers-file = "${ var.name-servers-file }" # } +module "dns" { + source = "./modules/dns" + name = "${ var.name }" + external_lb = "${ module.etcd.external-lb }" + master_node_count = "${ var.master_node_count }" + domain = "${ var.domain }" +} + module "etcd" { source = "./modules/etcd" name = "${ var.name }" @@ -113,7 +121,7 @@ module "kubeconfig" { admin-key-pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" admin-pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" ca-pem = "${ var.data_dir }/.cfssl/ca.pem" - external-lb = "${ module.etcd.external-lb }" + external_fqdn = "endpoint.${ var.name }.${ var.domain }" name = "${ var.name }" } diff --git a/output.tf b/output.tf index c0ffe27..425cb75 100644 --- a/output.tf +++ b/output.tf @@ -12,7 +12,7 @@ #output "cluster-domain" { value = "${ var.cluster-domain }" } #output "dns-service-ip" { value = "${ var.dns-service-ip }" } #output "etcd1-ip" { value = "${ element( split(",", var.etcd-ips), 0 ) }" } -output "external-elb" { value = "${ module.etcd.external-lb }" } +output "external_lb" { value = "${ module.etcd.external-lb }" } #output "internal-tld" { value = "${ var.internal-tld }" } #output "name" { value = "${ var.name }" } #output "region" { value = "${ var.aws["region"] }" } From 5589c382221478f954d895e03e6e3490c725b90d Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Fri, 21 Apr 2017 05:31:20 +1200 Subject: [PATCH 105/149] cherry-pick gce --- docs/arch.png | Bin 82510 -> 0 bytes docs/azure_app_endpoints.png | Bin 33939 -> 0 bytes docs/azure_app_registration.png | Bin 47889 -> 0 bytes docs/cloud-init.png | Bin 37106 -> 0 bytes docs/drafts/writeup.md | 285 ------------------ docs/guid_from_oauth_endpoint.png | Bin 51214 -> 0 bytes docs/k8s-cube.png | Bin 29569 -> 0 bytes docs/k8s-simpler.png | Bin 48901 -> 0 bytes docs/key_generation_copy_me.png | Bin 68431 -> 0 bytes docs/research.md | 61 ---- docs/research.org | 281 ----------------- docs/sdn.png | Bin 59817 -> 0 bytes docs/src/arch.graffle | Bin 3244 -> 0 bytes docs/src/k8s-simpler.graffle | Bin 2907 -> 0 bytes docs/src/sdn.graffle | Bin 3321 -> 0 bytes docs/web_api_application_type.png | Bin 34235 -> 0 bytes cert.tf => gce/cert.tf | 0 cloud-config.tf => gce/cloud-config.tf | 0 gce.tf => gce/gce.tf | 0 init-cfssl => gce/init-cfssl | 0 input.tf => gce/input.tf | 0 keypair.tf => gce/keypair.tf | 0 modules.tf => gce/modules.tf | 0 {modules => gce/modules}/bastion/input.tf | 0 {modules => gce/modules}/bastion/node.tf | 0 {modules => gce/modules}/bastion/output.tf | 0 .../modules}/bastion/user-data.yml | 0 {modules => gce/modules}/dns/dns.tf | 0 {modules => gce/modules}/dns/input.tf | 0 {modules => gce/modules}/dns/output.tf | 0 {modules => gce/modules}/etcd/cloud-config.tf | 0 .../modules}/etcd/cloud-config.yml | 0 {modules => gce/modules}/etcd/discovery.tf | 0 {modules => gce/modules}/etcd/external_lb.tf | 0 {modules => gce/modules}/etcd/input.tf | 0 {modules => gce/modules}/etcd/internal_lb.tf | 0 .../modules}/etcd/load-balancer.tf | 0 {modules => gce/modules}/etcd/nodes.tf | 0 {modules => gce/modules}/etcd/output.tf | 0 {modules => gce/modules}/kubeconfig/io.tf | 0 .../modules}/kubeconfig/kubeconfig.tf | 0 {modules => gce/modules}/security/io.tf | 0 {modules => gce/modules}/security/security.tf | 0 .../modules}/vpc/azure-security.tf | 0 {modules => gce/modules}/vpc/gce-subnet.tf | 0 {modules => gce/modules}/vpc/io.tf | 0 {modules => gce/modules}/vpc/output.tf | 0 {modules => gce/modules}/vpc/private.tf | 0 {modules => gce/modules}/vpc/public.tf | 0 {modules => gce/modules}/vpc/vpc.tf | 0 .../modules}/worker/cloud-config.tf | 0 .../modules}/worker/cloud-config.yml | 0 {modules => gce/modules}/worker/input.tf | 0 {modules => gce/modules}/worker/nodes.tf | 0 output.tf => gce/output.tf | 0 runme => gce/runme | 0 wait-for-cluster => gce/wait-for-cluster | 0 modules/etcd/.#cloud-config.yml | 1 - readme.org | 211 ------------- 59 files changed, 839 deletions(-) delete mode 100644 docs/arch.png delete mode 100644 docs/azure_app_endpoints.png delete mode 100644 docs/azure_app_registration.png delete mode 100644 docs/cloud-init.png delete mode 100644 docs/drafts/writeup.md delete mode 100644 docs/guid_from_oauth_endpoint.png delete mode 100644 docs/k8s-cube.png delete mode 100644 docs/k8s-simpler.png delete mode 100644 docs/key_generation_copy_me.png delete mode 100644 docs/research.md delete mode 100644 docs/research.org delete mode 100644 docs/sdn.png delete mode 100644 docs/src/arch.graffle delete mode 100644 docs/src/k8s-simpler.graffle delete mode 100644 docs/src/sdn.graffle delete mode 100644 docs/web_api_application_type.png rename cert.tf => gce/cert.tf (100%) rename cloud-config.tf => gce/cloud-config.tf (100%) rename gce.tf => gce/gce.tf (100%) rename init-cfssl => gce/init-cfssl (100%) rename input.tf => gce/input.tf (100%) rename keypair.tf => gce/keypair.tf (100%) rename modules.tf => gce/modules.tf (100%) rename {modules => gce/modules}/bastion/input.tf (100%) rename {modules => gce/modules}/bastion/node.tf (100%) rename {modules => gce/modules}/bastion/output.tf (100%) rename {modules => gce/modules}/bastion/user-data.yml (100%) rename {modules => gce/modules}/dns/dns.tf (100%) rename {modules => gce/modules}/dns/input.tf (100%) rename {modules => gce/modules}/dns/output.tf (100%) rename {modules => gce/modules}/etcd/cloud-config.tf (100%) rename {modules => gce/modules}/etcd/cloud-config.yml (100%) rename {modules => gce/modules}/etcd/discovery.tf (100%) rename {modules => gce/modules}/etcd/external_lb.tf (100%) rename {modules => gce/modules}/etcd/input.tf (100%) rename {modules => gce/modules}/etcd/internal_lb.tf (100%) rename {modules => gce/modules}/etcd/load-balancer.tf (100%) rename {modules => gce/modules}/etcd/nodes.tf (100%) rename {modules => gce/modules}/etcd/output.tf (100%) rename {modules => gce/modules}/kubeconfig/io.tf (100%) rename {modules => gce/modules}/kubeconfig/kubeconfig.tf (100%) rename {modules => gce/modules}/security/io.tf (100%) rename {modules => gce/modules}/security/security.tf (100%) rename {modules => gce/modules}/vpc/azure-security.tf (100%) rename {modules => gce/modules}/vpc/gce-subnet.tf (100%) rename {modules => gce/modules}/vpc/io.tf (100%) rename {modules => gce/modules}/vpc/output.tf (100%) rename {modules => gce/modules}/vpc/private.tf (100%) rename {modules => gce/modules}/vpc/public.tf (100%) rename {modules => gce/modules}/vpc/vpc.tf (100%) rename {modules => gce/modules}/worker/cloud-config.tf (100%) rename {modules => gce/modules}/worker/cloud-config.yml (100%) rename {modules => gce/modules}/worker/input.tf (100%) rename {modules => gce/modules}/worker/nodes.tf (100%) rename output.tf => gce/output.tf (100%) rename runme => gce/runme (100%) rename wait-for-cluster => gce/wait-for-cluster (100%) delete mode 120000 modules/etcd/.#cloud-config.yml delete mode 100644 readme.org diff --git a/docs/arch.png b/docs/arch.png deleted file mode 100644 index a0e41ce820867bbcb818c4eaa67e6db971dbc4c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82510 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 L?qvUM^6Gy99BP!d diff --git a/docs/azure_app_endpoints.png b/docs/azure_app_endpoints.png deleted file mode 100644 index bd4186016311835479525e8ba1d3af1aed76cf2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33939 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 XOe+`iNJHNs@K07!QKCfb!{`43_p$bc diff --git a/docs/azure_app_registration.png b/docs/azure_app_registration.png deleted file mode 100644 index 7701d57f25cf1baca9f50585afb805dde8cac2d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47889 zcmb@ubyStnw=QgFaSYB2f;SJ84Cr_RrNJ@w(K6&!I?a7m;4Y1F^FM0A~-r&FI zPC}B(u&}Um%W}V;JRy1_DI%!sp1QZ-qK>YG`{bxfTEd?Q;myl8Z(~CQgFizc9uvDY zjk_U+9~ZuSqH`s|mx5BFQcL7l6E&AhoZd6}uFi)WBp}CKUS3`8rE@tA@tB#q>6s1> zD4Mh4{23O!?89akp8orIl(M{k@WdA*JUm=OLjwi|27Fk+hx>c*Dn}6UUPo0GGffs+ zS-G;gd3d1y_rMPZgbhb;HFr;@Zf+ z!R%i-njbCP-(EKIy!i9W$ED}um+Rlgw)5mN#9+C&xm^vHZZ8k>Cs zx%pUGS-H5l7#NU@L z2eiXkoS-neTe0kZSR_MejXaP8@l5#!9*ACAOpFo_rppTT!Zd9lzxS2>$y)D0_%d|N zr2~fNvs3Gk^c6Jp={>W-$DxUC4|Cp6s)~xUbq+?G7tIc<-AZ|~1{ThZ!(u(%-3|5i z#E`S|a~T<#xVShJAu!{%wl*-4>8v3~#sRvVt?!R_Xt0^<1mcefoPN$HJM2@1cDByJ zK6ga#IP)s=v5=PPd^^;_YFQK}wmW|`Bm|vyCi4mIrMt#|{`?FHDRQDnt;%~=5dBRK z&4b7t3nP+?lG506N1$4<`Y-UHt)@yXtgLpooKKI#UTv?fWoKts8~36HyiqID9ULBh z#Nh9K_WZfoK;oxnCGgxG-YRNqYs<@zbOxiHpPzGSKsfsQ`)O!tX(=c;tfv{lI!;=j zW3L!YF$aMS;$84GaTC9tE<=fTd=0d`rTbI z37J3KT^n_Wwe^E%hefL{uc{iKlr$6kbV<^apm9Y};58RyvD;V-+YN4i3&<`sdOP-f^Jz&z?Q=xjD12wgwBUhC&ZZ zr|mn5c6W9vtEyC#m5*{}W@cPmT&~6r2D5?s#l^CjM7_0G<6v$Mxvqa`emShJ+0 zq*z&6o}8W<85yCWp_vV*5BB$O%UM-e)eoX{GvaZPrHtf8>@lA>Twy#E8wPeb9B5yC zWMyMhQC02k?Bw;myUOr;*dCSr# zeP29uvhofX15#3%eXx(B1WlKe1dd%`pq>A4C+~xNG$}oiHTV0=pKB9}pADz+kOGnj zuEu~qf=hlG@#EM~AI70~I-h{cw1C+7bOeYASM5Q3^Ah2W8ukuLe=c z$=qHS)*E@~Z@MNX&aaMFf$sJ7^)hA=13Ug@&2 zefR>06@kyA3VfdX(QoMldJ_|q{W&OufPkKy-0(Zh8rjt7$(-% zcWb?9dsN5ON}isc>gv-|WqP6_B4J@+D2?+vz54;>4(3TcmF)`EmItSrE#IBxC9vH=JW@cu*?7XPaayMC7B)-1B zbaW+P9pEz%kSwNwySsa5*PqwK#KhrzJsK{qmA?LuQc^@78z%@;XPVF`&MEzmfX|%F zU>;gfTRyjg`COTltE;Q>lkV{oaC`it9SHjbz)hJMq?x2PBx zI<3BZ4-K~F=A6JjXKSpu@XTBVxw*JxQrPLZxf`;xMc%*H|MBrDxKX#R`9PkIMtact z`uhG@-f*^LjBGk@Z+U)^Ui%e&%-63KRaH(dE?rLM(PF5ms3fGM7OXgvH6SpwD3!mW z)hnTO!|Tm=^o@#whiOdkkBNz?sbTBCl#3Q6I~4j>R56t4C;KdXTLLF7HWq@+%*JLy zdDzJ{!s#<*5=5ne=ValrcOtz;(-$J)OWFHNZPHj5J>}9?Z!26$byxf+!kwZ{?XN>x zw&aEheMBKjA{N`U;cX`+cI^Bx%2Fx;JYU@Y%--v~o|g>VMYQz7i|)60p-pb{hT-We zQ9C!9Jy;2~SM~FW8$l+$YffAd;){*etuk7=`!WXI%#oxgS&F&O&ya;4d1pARraMDx zwIJ6o6hrva!+bw(j=&#ofBEu-mzTG)vXVA~fZO>eP+2v#%k$l-)6L;Ok(`A^7#0|V z_2a|6t*tGoc!c-)&cxmI>H6B*(WRlC-67DL7N1*^+$PuVFl-PT>WDHe*O!M&m#3#nAbhs^wdUlkA1!w@H#L>#?M{}QUR;;TPEQ&VeN0w=EWz@hL%fE@St_c74X-@w6T@OhC_Q*-P8cw+b?l7KIn*W+ZoKnXZz zVIiTV?CR=z)o?{!55h4qv8t--$;k=O;Fgva^fxfWFxU}+nZki$ zA|mKmSgQtVDk>J1mdwWPu$f_D@OE}~xVX3&7;(wT$>rsY-ilJ5o;NF9p}+=Ege)v9 zP*6}h!%i=bp#?oYiiavHyNm@LqbB@`J1OpR?hscMjRZYX_=TD)_#74 zCC0~3>+BSPgM*XBWcsF9Ku%7+xVVV7F+xV?#M}^bjmXUkiN~WcG-#m_`RcX$=187wTHo-nI4oGF-C|SH0&w1asY?Y$ zK%vxA$jVuhPuVM8Ch0B1VbX4$;W{+YJalSb)ovN3&=9ceaqYIz3uBZp`2=oLBTg#e zwF_TAxb3u+YhhW}Zc)OE!6HN{H8D3cF2(SkEyQtOK`(#s$z0@SDg&z&V)>rE<9+5ycA=3i!@tK!)bEoiyp$zZGZ^+^=5FtX5GtqqUk z+D{Paj`9Fn>A|HytVvBx1wd3sXW`d(ap2+^=;?j8M{_`+Sm8qG9vwyHamvlgGBhv% zGH#0!F4L|@_4?7%V+O+D!=1;Tg|Tt=moG353$Ks}^h4X*+pTSEfY~4mMMp>1*4A>^ zEp`e_yp4GE=8K`RF*-JOunGKh0Z;s?UIm%3j0}R6>&|$=6$wz$JtI_NbTl+03ya{i z6kr@R<>f&@ucI?7E13Zvq+L}|R!(KJQcWqcFRHJ<1|DXbn9q8;JQRx|IVtJ*=qQT8 zP*PU5w5h4dKaFHaEU`{WLX&g`W}v#dTDR3VoX}2EQgXV;|L*92=(NDgqn2`(+}YXL z=_lmx-YsLR{8eXwt>Y3B#9@z5Pvz2iujlKX$c#dbuEs<^e1Lz{g`1jSFo5Szc<%R{ z*n$k0-_;P9b+0#6)wN9Z%qqR4?#fx2ncJ6+$3{oX=W&dztv}o`{!kQ)2YSSAGYdqcp{V#7nM6`* zwm3CDzSeT0@Z-lPOiWDCV(MZ5n?hvbtnLK@%JPma)w2dZdY>K-p8z1k!dp^YOvWyR zX9*eUi6p!|+a{%_*Huzd($)r=`#!+8!h+z+NKQ^J;0i% zK_7K<;iK=0A6-(t|{;gqM|TD`PtdVv(*+salkXL6l0LA?4?CVqZ%?&Q&V4w zh2yaGO-!ih=rr3cwE#87!oaX`!mu?lHAN)gealn&0tN;^x|F1(V0;R)(COtPnQw#f zb9e?TvcwkQ##oGCcBjY3nZL{p4GmdwO7inzd2D-;kmA)pin_sb z7P;cpcb~N-!}4TMFMqG6_qaDx1wuP8zO||B80CkP_5Lty7Tcv38Ak+c_QNC-Wi~c8 zkZMjRk)iH48mAKn>L)2O!m}OwBlvl*{X~I`6si;fd2;T}c610!O7^z53yFvTWNh-s zeRK6L4I>~Rq_EoL8wa zdN#DSwtoGJa$19pADft%2%H~qDG99X?1mY2WTEOnJq#0@79(Vt)<_4+|H}cmdw8(1 zvPQ?mAi%?ef5GSS1rbO0@=n`-v8~k5G;n&V&S5p{`*+~QQe$ITEypPh*-jkDm+TQA zbqy~YR+pCn#ICKVFyQ~R0IYv+Kc}FepssFuZ0r=ko$2i-wr;G?5%o&en(hBuKa^pP z{T|{*`~z_puVK|~;J3E;hOU)`1qfuXUcIVHsXqqU$lBUkhP@uakPHr|W6Yt(UPWzF z@^E0{J5SnDP6FyV0TP~G*@dV~ZAJM#nr1~Y^=Dqm?Y_*fC0Elz1Ep+iZQ3aE8F@uv zyVhE2ly4uRBl={2MJcplA;j$qDAx)IQ8u3Rt50FjI&HBZ{zL)KNlpP438^RVb3sv& zxQNK~?CeVD^g^G1LISkD9^20efU`I#6dDu+D?~|79^idCqf%O03Kq@fxE54f%U;nE z8#@jn#Bmr}%Ghu;IF^^U8G=Fc^Z8qTZ+CNZ%G2QYLmV0S0h!(W)3RyYCRSE+t$u!P zZkNDMq$DOX7RSZJ_-9NE_4XPJmB77uvoVn5e_!))X$+s3jE1JaoW4auSC^lU5C3*0 zomV0?AmC*!PjPYaw9U8T;=Rq`j5$0^3yba4RJ=Q3dI}10P0i$KSoX-OtgQBl35?q_ z5B$Mu#-Vh+TVOXgH#g0m=Oh*Y z^pJCKV5R7HAY-8PtI>2sta>Z=BB90$|JwK)FN{(8_n!$oox8N94F`13PHcvtL7qPr zgW+XKSZHWyc({o!4lZt}j>dE|MJ8Y*&dz+M)jUOwq?J@X9>|xf;{99ovUbgxm$=q$ z7S=XRmdww(8%Bohau;v;=Uf_p325C9zcOtpwz#^eXwu)V>QJxWexeij15U57e=FQ& zc}7zDL2oE5`*Zt(f&=ERu^~~@)W{UdUWi1iclgW>#THSsbF*a^)UO)3* zcpB-*bU?8*%3exPtrQ{4dF1BiqAnR=r;|V?OLcGw2qJXmtAMiynXS>fR;{%L)K*7b z{cVdm03);$9KG<r#p(7?6A zVQi)hBc95u0S}_4W~6t^IPU_txw)B;mbNf55|x&=e|R{|OQ#|wMNLfnw2^_p_n1V; z9009{hlfs=V^IK`0h_xASI%+!>f zl{GdYp|P>Cs+iq%Pd(sAcO+pXU4zr+5QvVrfF8oZ0X`M*W3$>{B($~f#`5II@`@K* ze2Q{&1N`PmAfD$tIk~wP7yDY#qMew@DJd4LnwFN-t|~MX9HT4cCebM52k0l0)BDd{ zN&S6t{F|3o^$w%(2=)nH{S93mMo)-Eauk8WFDxv)Ld5%dXZF08)2VWK;3fQ9WMpJ{ zfb>R2j;>G5TmyZaoSY;jgMraEG;nuhWt^gRpB*e{si$LurAa!iQPe4*Qg4 zZR@YWg+t96vVrZkHfMZuqqEFXl0LhSSlESOoVFF+RBJbZ(RdyTRIfT(D^6D9t?-}G zhdJn|s>l(NEZ7ybrFIZaf524)9xy^2uw#0*0`JUmPW-LQo@ z+u?M+KkPss$P&Q`3A7p@vW%7KwWjg7RW|l+0r;$1s-2XQ0szAN?5yYhY|T6Acur2v zpTPMgQTA?B6@@KK#$Jt)O?y>`hW2-dBYX zoXiH;*nz1jkYlQV$PLUyCn~k0lIMB?)Aj zt{qjRSnv)i`UlxLZ^_HHEKzL7#-OJf-~Z?e#oFE7&B@96XRjd)UE(QHIU%8;fJv^1G^NR6bHlyUu!e_kMQ zorCdx_9lF?JI=tF8$Sbg@EV_m^V34X_f_byw3HN}6OCH*h2-V!L3G3OEm#J=#a=^E z>>NCt%LWH3ad&+CV}=LP-hD^)<_>(hC8mX_Lgj8>};}7c*l&WGwjG`(z?3oAj~GSS#d*WO2r(jq!ADOU;9?pmZ5e~xHyaEj#6D_OpcEJVKxA>oY|HqxE}2WueR{@n`)m!Qjeu`6 z1(d!X(yLhtDk>_#x@Tnxfm9bzDSCRX{!pj;>)*U*cCQ?_4v7L^{w=BC?X2-U{Hqup z_4woA{QdFC!F$Aed_qD!po(td{|Y?5&!dF>^78ZF;^QM&u>L*G98Fv4N?21<6T}O^ zBDlT!cOPGiDB*wgnjiRy{r^>ha{JGKA9?=!=wg0Q{z1gQGd?c87B%@D^*_E2tpA(L z|GeUVUvBz$Ufpx9fAe}W|LK1$1mWM&|Lp|+kGDHLb=+E+$a9qaGU_7Zrx`hKxxP-5 z(ZY18u0ND?bPxga_r`Y(vq{2cjoskAiGOC9b5dTURB6wkT2cUo7lKuw3K9}5iM=?# zLQ~eH&5}u_!`Yv4^LOA;Wk_kJn`3LSo{S%g_*aR$)8uw0XsCx;m(()#i^`ymI_oHn z&xR!YY_Q&fwy9sq$cuaKDW3hpge6|Sdf)fhL7%cJ|NE>?QmA3jMa%Q`L|8Iy6s8;@ zU;URdwW*gkx9)epOLM+(9k(&G{~RIWy}EA9eo;DklbTf9vAhJPR22Tht}rwJ^IN{YPV&Xh&G$9jIkf_N=|-cWnU?i`#iFond$d z%IHYhYS@s;9hseWN$=;y|-Q9W5L2bfA-^fXN*GySpAt8m@@p|)SEH?U}5ub6f>^qsx=7o*N zavVnz&ZqHNk$-V6!{;w&;Gr;s9OPfvOs&3hF|QPLVReUb#-I^INN7; zvDszypad6xySXX~o)ju9QoPjG43zTQs~venx>!6F0eg|=6XbvsOy4%k=RC_0EV^gy z_!f_5-)sF?3la7p(WF} zrgm0UKAQ&%vR?Z(HeiXLk!PD6Ix63V%BFL9{W?Ess(0ELPU#Y<#1bz)`J)WA*P^6N&h+nqEk!B$k+r5GO~VsP!S_{jyPhyZUeKj z(%6VT9+ZZfiYfyCyMSw0OLUf@__Hm`c|?VtK=db1-pfs9;%Sm~hblgXvm0Ovdknta z#WZlJ`2DKn?(sV4X86n;qDeJQJvIZwbGT6_vXKr?t?QMXX}UnP-@b3lMgMhamsFXr z`=)=RpDRzOQYY*5l8-Kd5C4TiY=@?o!RL|+sCu55kViPgGvUXyj*oz8N!qCP+BBIu zwXj1&5mKgh84GYZW3$K(x9&&`ItXYpAEkh0Hb`R14^O;1ZJmyda*4_N0 zk7IjP50Z0r&C!pe_~+iu^Mp%1eRd0VJDd9`7-#^)nJ^{oV#BfI$_x%OP}0$*-8S5B z|ECv#f4_$?5RB@&Z*M=_e5#`;B=Qq(@AgHsBZ;io$G||d%OU*U*9>U7-2;jIao6v0 z_$;$CrmCu;!Nd%ANp5e1v4SQXhU5vY;*G7-F86r-F3xV(AaL|%<^B-jKc1dXD$vm! z81Yy2CCz5ZL6YXyK0(n5K+67PDv!Z*OV!pl)Fv zFDTb}=kh4gTJv&gmw`u{-OsH|j!LRA_k4j_lJ_^}p!)_^;O=1iiu%1&(K|gg{5)2+ zUVN=lw_URBe#D@_z!(=5)i6bk<9jQgoc+mZr43ZqIYlFVxsp=k4?e-jsIu^xtm7)U z51Y!Yi{JV}6nr?6PvX(#cUmR&7TdPpvqZiWdKY?`@K~dArSff*o#q;iCb`QbWHRG1 zH*;7e1(sM)khrmcm_ZG|50o}>`+#KaGY`eDeP8{1%( zn3!U)Y#h)Lt8G}~^_D((neey#cl(W(pt3j6%&HPH2ZmfYLj%D&RLfvwU>;Nm#u5ZKC&3Z@tI86d@H= zKs;Y_Qy>C9z>+_QhdC7GE|UVyoSaT?$E#5~0%LjxJ_==iNU_{JSl(XW=kY1za|j5l zi8E1m?@T-vb6{ANBjCv@%o5Tant-KEeH^~?aKguP!%j)I$gg_4sYDTh>33h4!{gab z2G?nshurwSIKrvZk|H{6KRY;=VmNid?vqe|Kwo5IK>%wr~ zRDoOefp3zvW$HhDPB5&xXU8qE&->g-&$nL4$-vl&DrE4y!rp;xiQJA$3*jqePt+`8 z5j@hVUaNBAWy2W1(fnmDS1}?k!|X$|r)uC*NPZuV9DUUEQ%zN(qzL0Gk;KcEG;p_8 zOPV1w`Rhh|nV!66GK$c6gJx#-QL+Li-Q8!}-Gm`0>uA+jZ|BdC{S>9#BoShRoO3{_ zNh(@a7!HocN3Tv<$TCueczH2gi|WIdJeEF1k1FnIAAWp#U(x>bUIqVQ!SUd;Ro|Q& z*)BPGo`{H;QRLxwfc=#%tnOJ^9~tUc>S-gM5vyBoIUb zuH?A$HIovnu}g)e&KGjmmUH#o(jhT8@;J1g1%wWNoS&|lLEZ83tm-_G_ z%mc*r-WG8Jn8qzh2zLiUOKhY(*8!c0jhww*t#eHF2#jfE-gXp~s67zDHLvdg&?3p8h zKwhU^+?*j2kB_+7OEVA;%_%D?&mT7R%&kglE58+jy$whX4BT8h{=SW!G%$nf-!#vA zSCN(Vxh8IXbD$bP5LjYtZ0z`=aSm1@$g}5q`kUqzhkZzd(;T3bvtBe=s@d4$=Qr`( zD-A_R)(Z*55Z{c`1HtF7NFZ-oy$#HzB;?EEb^A>lK@|lQSW{ngJ0f$#{55MC(HuQf zRm?flICQv&B~t|jCK7A(MaSg?hB94>Faoww{rTyw#OX3UX=I_AGfiS*%)nO<>(5WK zl2~$^y&fONHuu~Pm&{CjQbiMOPS&MSF1|H2;SodfqWAjhah|>t?b=J#E`C(-H;Ida`x!Jv5*%^d*po8oYxHPEsoa~~Jtn84yj0_;) zspCSBvs6HYM*9$kB4n_Y9+mq!W-gGY#`*L{mWc%?Q2wR8C_R4jKf zoXB5NT6*9i4@&_I@r7&30EXdP5t|4tNy&oBXw*j2(2!7!we|8IP~CzhzVqg-Fp*<8 zEVyv>_V%`g9_YUlY8P7C+2M@T)7GB1&Y}_!5bzCYu+uXE1UXK40CF_)BY&WjiV6&I zY{nzsOV@R9@AoPgGO8Q5eDBuR*AlfPK1kJ8S7h)HK7cq-@8RKg!0dZ0y7zgV-Za?Q zdJYAk(~kn`6nCm|sh2rh*{3YmSH)o!l5r(>fKT-0Z5wie zGR{jZ-}aEXikr0|9fvgLUAnpnmJG(Z7QIQ$n2ccjZNG3)a9FfBBG)XbndDxfKh#xc zdd!bdD2y9B?(0lWsa?7E1g54>d#24JLi#M@*S8ZQ2>Qhk`A~KD!?9|-{bFu(mXLyj z!(@+~`fLdD=p;^sWml!Jc!b|gYPg#8@K07efzrs>@jVTe88(cgyZZVWG97%IlaxL_ z_p7(W#w=ZLQWQcK5=gn$>4ePZrz^z70$?*^p?1eN!Y_;VXO{u@<+I(#7LeP5?xUcr z-=qYIj$R3Hu>z0^>I4N;*lUVUOh`g0DcUM36M8CfeGg*v0|wmTde;^N-)(WAZc^$#&E*+|sH zSNfH2Hr{>1uC`2f`x)6XR`GW^++vvrY$kcqV`{2ZbS$WAD#!ljaM{RI3l?ZCsWTHh zG~3jIh)8ep)F@KomChkfymW;n?$S}hM%B3XKSdGhOqq`xTKGj)GJo5P{cs0w2p1c` z4UvaiJ|K}<@tNIAfL0?Dk5hKIC*ea8@wrOb3;~?<<4%i+OXc;21P1na49l+I&D_62 zf>u|*fVnYLdZy0pG5hZ5I&@b}TWPV$)A4-uK}XF1wv5Y>9d;*|JI4?dmJht}eg9`@m~weaDlz`-LY9KD+@i_De z5FIu3TgY9C6B{hU5?j;QX9_;WE}(+i={8^f{^c(oiM%aqP#>x>xoK}b(*?x*ZSz|` zym3<_3q?6@GnEZ=qvR?C888I>ONYVvv8KdK@5Hqh3l-xhg5&_yQIXLlI=lHYpwh!W zCl0HUG+30_pvw48>Y|;zN%MNVhPBFL#dpffi)m=y1_gbH4&_!I2=aOC!$X$!1`){h*YS^6Z%-tbf0AtEz%(9kNcK_js4MJR6(Qus4>}-wqLDbw ze9F|!>==BBflx4admKzSvAWl{v9HfztDg3zaY7|#6Rst+n?89j=|fm}sh&d`RNq0F zHMIIQ5rkl&)#;#JbZ?Z~|9I7Qp{~Kc_oVzNC<1y1a`L_!+*e41>*Y0(8 ztC)6#OS0COe{?@+hzm)^!V(?9Z=RO?T`Dq!Vq~rJ`NCC;ci}{ux<$)3(f8SsQkiEN z3uNbApAX@(yZmtZTiR_^s`5qPTJ_JTsaRuLJx!Xv9yLZxP-(hdq$JyhelxF%QjfcS z28ogvLar$dh;h+Cy*heBtZc&>zAnSQMLMVj0};`!c<^BPs-B@LXaK;4|LC_9Y+Mg% zxZ8RDABK%Wgv1c)l7vZww~Hi1B2&!di))a5bKcfLt>{aet-3=kGRhkn8K^ma=p5Ev zTYEdHqSBZuz25A?r@Ii){I)Te26|XAvCr?3!s7Z90MqLt_3VD^Y-Mw5630P5!%5cm z;1o-60sjw^awMFmBTbL9tjp%{N&V(tDVdsAZPAtSL3fmc zVHsD@V3JzP(WCKWn1#%U;xZ`iZhf|3Frl{{6d3Q;;c>ULI1!Im)?_Hbw$Wg7phs#B zxwL9wOtF5Do0y%oG~uaf{C*|cEGPSOpii^LRA=`w?{+u|`b~>;C8B3c{9Z-n#fujq zp{?D`VFDgE);>OSES=zCe9XvAbK(LYz-z1Gx2EI2kN>Z^y8ovBmNAggcMjfilF-rV zC@PLFDEu4n$>x6=)lvUvqx%2#Z2S>XP*J(-?BqVUANA*#yK+RwM~`@*{N*s6xqfCg ze)`;`ZM=2b(kBHU>5z=GgEgGyi;WTgmhgV94Zq2|a)wNAIIp+N$cHZ@IYox7)R5CR zwI}8k_=PW>_iaIuFu{*xm42jsw{C*G95Ve!)Yb3LefTc9_Hjn^;!bU5s(odwXp)UB zjBHK%uQl;S2w3`w@7eHY51S13cZMu&?&_ERc%cST5JbH@EcRl&JZs47Fta@ zxM|>=tiR?4+${3($ffcZadaPzCpn2D9VW!YnM^g-PQt=DRr$O^TzzK*FVpijS#w5q zZoRs!rDge7vlixtDV}ZU_KVf3O@`6!v%W%V+n!Hp;+M8H8i8b{8^re(3Jz8v1xAyR z5!>E&m9kRWw>~+A+q21Yma@_b>F^nEW1=Ws6cq<#>qml6W@B~SON&4b?ZKsI2|N@hh`CtX-j0}26E@(k@HnffnDfckf_~jmUP{ghfl*5qNf`}@$m;v zC)IHM^uVi~{9ZO{{dgERrieNo_(&0mo0I4Du-99_vbnLo3B|UzJh2K#@tn6NfrDpa z3>48Nbij7XdoiOiFi1{+w|Ut5pgYp$3cC3^bYB#!S3UHJVs*UUP+`?H6PX(1=G@S! z?*F4)5M%}@O|{()wRg0`&%X9M6K`IpXW__-Q3=wwM^F8dPcE99<}YIgJ-KJ+Y7g77 zU;-iOTpnR2w?K6qE191T`%4%G`K2A5B~2|knc{PMW(|I%LYGQ$@qJcm_>cpbhbG@a zDINTM2uCo!MNgmMNlItum0DSh(xRr8OpDi}f|)4npNW`i53kD;md-`^>7aP_&}lUt z_4@O?X84}fu8TK&NJ~xq8QC-8?eNjz%<8SW&qga@&^w?@cDye%HDNqAS9l}MQ|g!|`u6-i0J5gAhF#Zi9cAEPv}FnXurV$+OwLMn6KZP2qRzI~AIvw-3OaR*X$LBj z4#1FY#LMJj0opdSRRyl>av2_=AxGf699lz6FC{JcdvkNC+Q4QDuC?=OD-Y=q2N&DE zF7_$ZpNfIP?e6HQHDj4x%e0;f%o1Ct4;j_W?a`-Z1p@}u6%spL7g6i(RFgxpGQ*9D z;EJz@ZMw~#HrlP^KbLrz_-BWAx7)8z^OdO^&4DX8d(Y>|@0`J}$aA@eZ^YO(-v@d# zZ2P;oB0M3H=^P>ks-qV^zScHus*0QP## zgtsiuhb}`QkWN16ZU(;#-c=cLKHnXOhXF~1 zbK*VEGbC^RuD4vQPIs#=QcHsrMKNQ^;hSs$P7Fl z?~f)ac@g-sAlpbXw&3k;pPM4A5I}wFDW3gD77jKCZB=;@6H^n9&HBARE5vwQxd#(X zIovL2U4Mas)Yd0YXGEW)pT|hs*r536%zYo$4@2sI4380jFi~8X<-p%nTX(hF{3^1q zuOGCIBMTL&(Kh-Vwv_3;_wn&DHJ$vrnzu2KF1h$QHg>JHB&Cwyw=(4-q3uN#&n2NQ zrx&X9ci?qAuCMBKTJN~&Pr5PG><9(CZO_6}pm_KX_yV?J`T0R@R~1#ca#q&X#xpI^ z(T1|Rx+%DdYHCx`@1J-LOsH0KW$Og~l|g?vgPig#+_7w{+@@qPWh2!w^(r;lee6by2b3KgVt zwst&SQw&e$wm|~|zwZOQ8YX$Gk6XvSs}10OpmzK%4`$l*#X2>g|A_#gn-?vx{D+?P z0OWv#`8xD>XhLbFwbNC3CI}ri*4D+Pr5-l}F~-u^p5ps_YqRtI$7Q;DbGHR9Mt_T9)X}kVfeL8H_>eG>~8mkr*8d{O6k$%S=e-&xM2pBr0V!Gho`7yI}_Q`ng z)jb$mM(7Y1Rc>E`(!r_S_wlrim7Uf759Y7%*oD_o6JQnYH3lSzlj5F zaki%FR*=O+c7LX~0=1E`vA5s7;MW6Qt_pRK932IPf*p&;seIRpFH@0ntHW4@*Kck- z<85pmrYk%;8lGgB$)$VNY1^Bqh1XxY5;_8FPfmWk!_ZVx>KGm#wy6f8?Ll9AbEH+D zeV%`=)_S(i1UKEKx4#!9VJMw#^%t63fYmuL8qfl>?aX}dAH^pGU2h=YYAB-CoSgHmKxaG^G$>?-o_@zpzUntJP7#& z$r~(*8~9BU5)u`#>PZ^K=w^$FiGkOwfw{9Yi_MQ)1NEn!?IkJfpc3_)i`C%>MF@n? zP}n(%me{A8{?{cJDY3?f?F<6RI}0i2zK<6rwvU}Yi96MubFk}4g3WI_x3=|#ToJIzq`%>XBt0VYXGTPF5U}^@?Bg#48QK821{;5<9NYQKh}D! zZ{hwSmtSvx|MlJDtkR9b9FF|^?dw`U_3la$=RQo}%s=Uf{MWoSYDFiYNyQwK)wP!|Mi+PH;!RNB&dq-;n_iR&Blay4*+~dN$sWK9Q09yvDLvPkKxOT4nfkFaW zV+jFE@-aIM==TM0$I41DZW^D9_kkXZTG`V0^mymKYB{cA=lY1xwIZvcoVbv7YBr+B zz?wwu&-Kr{m0h9MdFsa}Ct$C~hpNZp=Np~*Lv`ATJZ6*gGb~-5B7m^ox5*nwVY!{} zl3u&Nfq9{soDz=DGTX!OQJr7aPx-EBDph32IwVe1P;j~NvH*TPShBZEv`eX&wmk22 zoH!B$JgEqq>r~A)@Xj1F}CHhS@dM)O`MOAfm^$#DM0TkPaA+NMH z9_(9h_L7z_81FSjtC*2WWRH9iEetjrUF-{<`~^sq0F7Q5?K#duoo;KRHd|mbK?@wv z-fpn~A_%~FhegHSYd>@iP4+I-r|jlY(J-1FCR9Esvp%SNUM1K(`t~F~+$bx{NM}fo zw|c1{QaJ-uM_xtFwWtG@*Y@~Ch38=bm7qFEDyO^Io9#B^%QSR3GK1gB#%5u&S{ek# zZ{5%N{jnkgk4Q7P2G1*8kL4@9mR&GpN9SMYi)h+&NOUn z=ql!&Klgs>5a)tVZ&T*M)$^Mq-Co}{v*TI6Sh$7Fl+nU>wO!h1vJzKPaAK`F<9AzH3Wx2&YqI+Sp%IKT@yI5J zE|=~-kIu2~m=7|lv|7wIlk=s!33n7_ej7-JNI0xV?MX2)v^KM)fA+)Q&Hf2se3QLn z^o?$|NRdG+cb#-w=ohu@hl_crXj=e7muOY(935WVuI;m5mL=`k+P(U7ov!DB_Z9)o zgNu;8#8!ljJ?MH{9q1uDoMZjcse%1Q)}}Y+;E3rt@UeB>1KB%E-57?5fKri z`N;dtg$R%Vm#T55S^R1Lx$FCLyY#*BnNu6JXK6j_^W#e>J!nvK^ zBX*XNUa4MHq25vp=u8pq()*reWIB+prr7r@^-n4EH&)NN_twF)(|=6D4~AzO|HWqf zzz2zc2Jp;!*(l&d0DFQ7_y{${tso`gHIaHhil+AK4fM=vtnI9v&I52a!+8!}tu1X! z3rZz#kBB*0n=1J0&ddJO3s6ykAMT6~mT>+Dt}gV?9Xh$Pt14cWl-AdCQL^sI7^13< zo;+sM($mrS-IE`i+|fXyA|_#d?iH}K%Qz2RJ2&2-S;eaN?|yM7 z3W-WD9eR7O0H3h?kFQ80@FCEYQ=?!x{a#u-Ic(=~dQ)??>bll)i-_0axtg1uz4pSW z!<9oQEYk^}*Lz?}V5DypkAcg1uXAwDja(-6V*)23XV|@0MshB@Igu)-aXHSZ+`%#% z3o{GoE<_dr+tdC0R1{E_7n|Q93l$dLGc#8;m}~ugMv3S1c$&Jh@sSxzkJnBf9$ca@ zFp!arbE^u!MtpqsQayiU$Ho016>@$?cIN5raup%%y~7#5673e_>u6Enhu7#rgM$at z_?-H?U98kfesH6rVRa~}7as!*yJc=g3;_UNyTutr@-}g2C;7)F!+0S4GNVxRTL4`C z6c|j#ZZP`eDS!5}R9AU#lkn-_Lq>kLS@tK;uh_c_m~Bb~1*1q`U0qvkJ8vW(Dk`bm zo`c;2o9pXfX+D<`Oo81wd1eOuU~hL}aq++)!`qQo0U?PRyVfxHf4yZ#r)MDi zf&wlRQ-z7iI;dxHb8EtU`9d*NM+5;As!cL(QlvbS*rLaCnk{arUf_{A_D^{C>SEY|M)9nZegUs%q@h$kBha}pBtJSby@hq<-PY6!&Y+FtnsFZ+Dk%2WUyyl-g)qp%*T&npV{o+|UD6nX-c}%Onef#mk==FWB2dj);E1o2HXqy&&9!xD!&A0 z!oR=X&3GQ%{}=UaPWXT9H~nu8`@h>)?_^t*5&^@!K>Xy0=H&QT5LQD=3ydhxo4}y= z=Wmw8HZ^&>1%jdYVEz|pZyi=;_iYWMh*$_nDJh$llI~D8-7T$jZMsuLT0pwHLAqNB z>F(~_bZwe<@%*0iopY}5pZ9vX{Kw7aj&-kluDQk>W6Z>VDZO8LlA?tMxDO4-12~e# z{$=zw({ZBzKQm$f`^uIs9iMesKJ2sDYVG5mbr{VmS+GY=CAiTRpykwbq`T#$Ak8Kq zyMHY_P?|?9-5rY-XuThJzCNcdg3mS~23toQ)AJ6y4T9=;?(0RKbVatt4n~osEQ8I{ z7lweuXuB~__FYcO{2F`*%%voBLejhjx2=}Es)LPtc5l%iH)AA0Rgg99hU1t`(GwN% zoJ|L7Bx2jN_y3jn^r_Q4{3%rw*y-7()t<6mllUdYYsk8uPbLLXV@{VCh_)n<7pZ{j zJAiN2+H!G*B}H7d%^ka&#+UpA`qrO57vy@ahjT>ONX>rN-&)R9ffg7fZGq2@1PT|T zdT^J@Isb;d!Pz{nAPT077jPW^k(Y#zmod_T9!@t`GdA=&@m8sxMhpMGnD5|yJZRqN zal<|%z2oCsGy z?cm#A$e~x~L@(|wJBZP>;DfER*8h@CS;)!W+BW90nCb25(NI_MSUunR@#9w<%T55B zkyPc)&*Tdlo*C1!sb!yIjE}79r(6@XPoIi|E!6RPFKYtpgMxshT|i$_X16z+=?e7bBaP+hgwa^N2byT$zr7PyF%^2o4SM492K)J6)eB zrcl)ykU(q+X>!o4&4s`caBo==V%6lky)Y1A1v)_@Pc1y{7~hrl=>VF6=J-*Zu8|w zYHDx53emva)8bHXyDyKHM2tlciV+wPOr`y{^Lk(|;*=MQ&Xb})i|VtZTbWoBJTy=Nyc;xWw($<*H$$CAeQ_$H17^W$ML7T{_`g-0fuvzG#c z3HlK;zglZZl!rS&-kRKFrc1rREEO99QtF2GEPoK>*nfIg>pDp z+3|mjad0G@^Y429OkFgId47T7qYxer3^XXJ_VF-`jo-^iNmT&F{Bl--nF>Qh%$2n+ zBMsNua1=owecz13&U~Ho4M_D4LYM_S2n8Hol;#z1SnN7Zi_@h|jMoVqPnW^~PpkU- zqr$+yOPpVmv-1OS@o|B}hX252CnI&{P(Il9!{OUN2@IGb0{CjURS8MKV?m39A+s^w z6c4<#pO69+;=7!gBsfl5lb69HuM+WhId?6-kGVkF^dv2M9d{=Q=_*BH&7|Bq9l4)J zt9W)2z#yfqu6`z_RwX_GQiT~X3-ags~PifXu*;5!tD-jzBoe394GBK(S8fAWq9 zyFHKy=Mgpz_OB=Su9l^U+)^`ErZWqFiZfz?`t-@4*{RgHk(BA_C-Yx`UBaf}oR&)b z!%J5Z;*>ZZ$Gt=zo^oPe9+rfvy=#n=&!(oYFyG8Aw8DnJm-_e!n~lQ=ISlp7vsIwv z7*G7EnVDTSe|i?rqy-Z)(7NEpk>0qOc?-P2{daSU-3m9+-&3K z6`lI7vIXBB)Vufn7EJLE507xIN3UCVw{0u=L$AT!>^@<+dO!+b_~lER=RJiA$vyJ; zDk{Oq-oz&et9p`axia2W>)d(`#$q^!8eOzKJQ}L6H?lr|gL*SJca6MX0E!EDV?Cu8q`0=oDlk2Uz3S|cr z*cGaa+QE`Sz@FP|GZz1&w|9o$(b=sD5v<`CLWrn^cf6>Qk&@u|*j}KYaS#@6&R5KF z_^2CR7oV@OpA22Kofyd0BB1o5gs>+h{rs7mm$fzB7w7-%c@TLq6PCOaRR1_z#hg9C zACi%tz-mEGpa9iZ>RIpe7qbxp!{<`x0`x=Bio3-yoPu%l$uhoo+%E0;Y|B)q{I0hj z$svdI0RWDwY+f4aPBpA`n(Fz}#1|om6;>M;=k7Wa6w~N$3@UHtxCINpmrV2X?+39U zddJBaOhGCA@=p=jee%i|JBY5o!T9|;4a?nZ`*9+7p%x8mv0o zy-{YHiz(O0-o7D;g3t^qsU%)^i+c8PnIxP!_){dLg;!t<5e$3o>gsZw!yXSVmGDzsR-zDmj`XQ0*hCGp!Sbuc&y<0QqOg+-f9=YQp`4y!ViO2@nP zCIC(2RQE86g@eP(ln#*3>c+pa6tj7MvKp`wV4Sm&9MzvNWS>~E`bWnVh#m2Za z&3#f2i2yNjm-Ee1Z}+R=oo8Pye}UHw#Q)USMM9ETRD->z>%#)i)Di0vEh762gZ5!IJDUgGXMMvs z1-(+zK9I1GsNu7ynEB?%@9il0=#cjZNZk@ zs@>gCPaH{DY$ow+H;2gl0{x`iP*M zUJaB@nIF2V+LV5xYsVqyaVHL>spkLcDkITs{xRqLTK3`P zKRZ4-_J6mX-QaLYztR8v9&Y_ToKeSyf+Q!jbaB5v$cu}c8qSVc5{kDoxq}oB87cA+ z_^#loGT(9k_8T0e-__s$=lN1abRSdT;aacLO&d&^e#&4ci@9k^8NlwDZ+TkF%kN5O#1BE_xxdlT&jeLTgdAmv zh_5$9JL)@pc5A;&&7>}Z4mehFim3n{NXtxBX1ZdfVs@;0hr8TkOdos%Wwn^TjKQ=F z*c5WhH*3BCS2Nr6#jtlILSv%uqz-dun~9N>sgN+tAJs$uua<#_zw!DvBG29W_chJ7 zpAME{si>kE32nDM`J>83!P9&C&*~a;K3%vftSQb8D>R=`e}litaBJ`gGl|>Pk7l8C zBT?|JW~s5%im!b5o`2po+J<4N^Cza*5PDn&waK>HTHsa&x#d3?-Vb{%lAjpd0}y8XPJ@!Q^v33-sT+6d+BX zpWAI8yR>usbUnkr@HJ=69}19Q>4)AemoL<3N7{=#7i7SNz$b>~M`giGemnuN4Ab{y zLVSiFB^8tWkB*+jO+}7ML0(a1UshJE6)_aaNy;YpD=3uvQs|wnq&0qVP;}%CtEdBju|7=c!5n#deRh^b3D#9;%c$99glj z{LiN8vQXd4I%s5a7=6)SDV8;ZU?>BbP$RvkOsJb%FJe060kPK6^T6n$d7aBKFbtZ` zHpXc5@+<3iH*VHd!`?su*KR(|N}7%u3W=Rc^8~+q*RGWBVM~C7gl)nT@i=V zEvW+U6?Yi~^C6O8+zqq)f%1N8rA7#crKvdd4=JWi0Scd6m(r)|wMPkk@jNgLO!_#E zqpJOtN)4IO%J*BJCyxGP9N25fz9tw`XSP(j_+~O!_Qgu<9JVkB9?hgVJ0{wjYL~y6 zw`Wy-C=ZvGuqFa>gEH(d@KKR+U){%!)XQ*p3{jc|Oyjxyp|obayVAp4#eJ?2BK6%V zcFQ|?#wpY0-q*}}w^fTj{kbcHU*0rM)+v)g@>M5BH@d6PW@P)cPE63w!Ph=X}#YJ0tuw#kZEk=>yU*e6DITI4m zc6*a`wjJXXmFP%vH%rMM?}OmiGMWMV&F_fS3%}fx^7~Ja79x%`{S5bLum~@;>0TWjC+0Hzy zH+RWEt5k$KPjuO)YcHsao~|+Gq9x(YH*H^Y!)V6z=9Sv~lw=XyQSEZKzUblA&Si$m zNh)$Y+{g$v4jiJc>(e1nMJn3Y3@ED6uMF|EGaY!k{X zkE^OOD=Tv+p6~KshJU+gBK3Rxb-25$ORL7tfNCST@$zMZ+gki;`Vz=|6HX7AUfSZ( zz2S0zh+ikM((<^j&*~6y*UQ8)aq#fu+@)f|IV>pBcXkK~P;&qq9CN69%=3(Z^BZ;kmSO_&4DmXI|ow#SpFfoc`uBY(X%89AtnB4C|N@vJX zt~K2r4&em!YaNB>#*wZiu!Rf6cX1ZNB-fqEN)5si^R(5X&s7XF?s;d0Frxd!THEkr zgDwr?6=o8@ZAf1BqOCke{?2XK9I`?J^~DQ?6Ew21CyESX?uU@PmWJzZCgtl>j@#^4 zF+Nga6z#t^4RWuG#9oJAS^sM3N1vUtR}SLO3SIWX5dIjC2vnqczwleYQs-f)8i-Ak zRyVvXLpPhL*!RYBxvj7;cr`m%O=>g7POcdriv zZR+z@BdS-~awbMT(l0c4eGzVmzlJm&tq%5Tc`v6h_CNfM_%bq{1dKys`cv@vY&P@R z;0sMbSd940#)6$09nKdQC@4M%Z{i>L{3qo&)jNxaPSwRafx+&sCv98AMRUXg&Js|- zURXT7nCGy>8Uov(IjuyB32qOk1@GTHSBRau-4)lFj!>Ex4J0R5-C51_I&KpjzZn;a zF=10xz;pSc+sCs{N_x%r=EQk&*{AusO1JAU;_kM|VP%1*>blhQ0!9K1(S8qG3Rg|9 z@79BjLQ J55PJwtcPD@1@)Is)>w@#fJ}H7H?89TOY^3vCh*Pov-$MC!D*&n{VgA z=^aBisstAZgdX2ODM->4NOEUemG_LO=8b6tEZRjgX1#ZpG6wqy#(R_4ZU%?2zA$RP zqmG2!PmZ_`YPLqwSfTxwuN}6|USaN@%+56HEF?@YP@ijJe}7l+sgL+NN-4qZjqTam z8LVi5FnErl#N#~P zeTVor{oIOhw_g})Z{23Z6a3d3m%pz+gK(P;6c%vPq;uxWi9vg`@j^LN-6zBo^5`@~ zSM!CSaDu!5O?&bjifUIX#e-xre1mM}X-$fvPpf213>OxZcT#4-r@><&UXZLl!N&F| zWkk%Ftvig>>SZ`HM1izMN8!Cqy*PAYm`ew*9f?pZgnxp^=G+z1R-64$Lep zyHj&M{`_rJ7(?DaMfvLMW8>pbc1I*FCJQyHKv8&ms*wPbYjO(MP1f1jLekH_x82VN z_09~rsa=UvCC&MIBXr=MDsU&LbBBb67Cg_V)zg6Rbb5KV3ry$8B`s!;tl?v=`QmyM zS)ra1j9VB{mx61=MK%IFha!>}*0lWiYsBVS+>XGgac5&m1iFMN|Lj>-r-P2gRhdL> zjDN0mhOeXhjZg|NZ5~Z~TK68w)RQcB`QbO2=LFfT*3p>>jY%E^pLBfQN00Ql zkWwoixji^eVLN6FYR6?92CSUTnU0Q$yLI{$Qg06h`HP*FA7a$_0uK70jO9KV59;u+ zbC{VLe39Rn>>45AZ(~$rl@(TA`@N)Rn4L`@&k><%)^#-YG3{Wr6d6kXDu5p*mgKLb ztQ;Ulje&ug6PxktmlU*DC&d7G;9+87g#*(qr+Br-NR6<RsJO9q^G!xhb@v8B zGXYa4^&n_2W~S+v!X5==8eTg$Uvx8|Xz0sN&%~$vPcRRbGsU7v`ViRsMFPu6T;Eko z;f1bCH|x0OS|~@Nq)EdT?$N;7$F#(o;$xFpRvmKP>{!pZWrAH+trwB?KDdt!pG)Lt ze4>=K;|eATr`3P#3dMcKm~~XnPZZECApun)$G{Aul$2k>yJCLVAolhZU&RC@JsiE_ zRefr^3Iww5i2f&(JsKIwLyCxJdZB40AvxK4X&7kRa9nvby0U~Gqt)PHU!Ql4q~fLixg(b#>%ct^VrI zWNimZJW=r+?IypS?SmPS=H#my9hqKD>#)Low++k%=ihbD`Qt86XWOyD1;TxrVaFug zyMhdrn2bry>z6goR6gn<6qOMJrW?GU=XNoYJ@^pBtyMd`mmddOr1NF=F=reIp9V>= zV&Zid(o~a$??zT=)w+kl3Nz8OY;!`u^5Or(g`yu1ha^2*A6*qCMQiKduuQ)=f3vfb z$YR7;^6A%E)$V;9yDHlSzFHpsvKY?<>PJ;C8#4*Fd$Z26@NWj6`FQ*NIpNp>zv0bFN?fpn|G~W zwD}pLDjlGC5)~j8Bi*xI@HC+1`FD!3u*`n&;tbSTGskt{$xGnD3`;~csbIn?qHhivBP$0*+L+1&Z@Lqx_*30KZp8) z$M&oR5Mb!GN24{TGp$<MX zg`qdIS5};wQ-m%jonJUC=4rOjr^4k+)qZ&(`xQjNmo4s}IBY5%rPo1?4i~d@(mZ@Z zu@oH595(GxQeB`pqe)o1bE{eL*}_F6Tci08KxDJ_eRdqye5uCE_rKnHi*Qa>?vJ%b z`BTcgwq1!1jgU-XJX5!jZb$X~w%ffgnWv#q<>ZxA*q51C&h5Bse^{m(Hy*{OiR0#= zv>mE(I7E&3Y&QREYcgO&=5Bwsto`}6`l!6zdHbH{*Z?g(Ut@&rz^vvL6&CFiWrw>? z5o&ue>T_&tx7J8I&8}S!anbs3B?fFm`n7M~4TL!tCB_;lo)%r2$55d>zIFC{<*Xp- z$fj(W-BtFidx2=+s=87H8I1x`))2D*;apHo;Twh4W^#u|kE-%Sw6GlA?PGC8cG|Li zPH=M>*5J>fnof7K2)NDBpRnX4dW-rnBioUB%r z3m(8rY_tk!UE*{oxic)4&ujHoxC}#V^v&?|RKtF4QzPYv?ZeEU0X&CEzpcj6+8S_^ zr4v}EZYET2)GM)-_6FN4!J3vZUW_U5F-_eERp+4`kvU= zZ}n%}OdLYAU_6|N!`E%t>NDI`mGIu{_vL0B=9tC0F!(10Hqi~@{vwMFMr};hCabOd z%EFcAJ08Y*Zb7z4#T~{N43%dS#p!pI4Y5wX8j-*y+ z)zp|&SaoK!&*pTtne3zvNXAv6mZ}>JWucByzeJ!PZmV@Vn%)RlU#32|i46xzHT&&N ztJ~$MB_UMzBpD>7>`F_nA(R$sQP^`f3W#Y=t7%S#%Gd`02`b^$yp)EXOf0zGox|cJ zIjHg;sRxRYtJj(jSr$yJudaHO_O-M?kdfUDxtX_>zkFE&b?! zK;13gu&Z(iQFBAG>~j_;8=ZR#F}dKkev}XwJVY8SVI11_qvI!`0V$W5x4&jRm!r*2 ziQyb&9&Tnn7dIl7#c~M9C}O9;z25Lz*tz$(M4xUaH#=PvAW&0V__2fVyImRK4XTNQ znjfH|yS4VpRKnPzIM3pa;9J+o^sHxWs?D)~o8$jdr5h9nbtDut!7~e;g#R)Y2i8J{ zuKKx`E?NrrnLYyYNj}f&AQWo9JsRSE9O9qMS4YM6&YLETP7}}u{5x|CCK_;*>Q~%r z9bn~)sOSN?B_#*zg*tdu_7A_)thDZViLy@ssymuxR!tkpp08d-6fM+@ffU~l zvCrS&>DZUQCM+xrn92DIhIdyjW|Os)yzW}aL?Fl}d9?#cv`9;usCKos;aF`q)A zP>yMny4Tc>&*@+E;!=MKBPCZi&Uaks$tIhhzY9KfisTxVtuQK9t*OLny3VqDSDm@K zijK4xw)WfW2w)bGGpy?X?>+Qx%lJ(i7({e65MN3rtQ zUarQCP|+4uec9btKm$^NfEbBF6uCmy@y~n}BkGtv)~@QYjBio-&Kzj%A=6_@?h^W? z%5qJ$`0#~se6-czIKxl#J+kY8nx|7j(vi(I%R16yR8Z_n=TCByY9<9ymroSk*d8DW zR%Va>s+3mlkb4K5`XBIfZyx5K7Dr9%Vly0d5VA84wG> zvAXkkxhlfEJdU%2fxqY8<3~&srm(uanN!mA^q=tA_X632RWLK+P`o!c+wFu+L+OX2X_?wU-|5 z{9}#xte@+WfRR+h29uGLa%15%{e8;qh-anoP&V_oNJd7+{o>}^>1s$bFIWHE`;JQq9Ct1R|13Q}m2Qn@FvH5Sqn#0d9<$3l)$XtjxezBv`^(c_Qq-i|B=a(q(xPo-p0BfkQH zT{c!Z3M`NdDu*$i`Dil4^sGVr9-p6`yLohG41&lB;w1$sX{o}rW`lXuw;2L##==|e zh8Lmk$5n3d#{y4i=;*@V%{IaJ5hTmBgm_3wA>t#3vhnO1Wo^^rhd_NCj&;D7_h(1Ow?#ez|8bPGopI}$~Q#6e<^dr`*hiA=`4rS~ZhE`0CoaA?4qw;+tUr;YhvIQc_|C-6#Mz9*u}Qa$w`RPfKxWt>n;_ zPKMgo_)WlL16j)tLjKGG#SMU|RF_(`N8({)A2{(^v!%(!Mn%y#*?aw2IpiV?apI<> zO^6He|6w|m9w+#=v%4QR0IHy&AN|bdW38lQO9iDGgoB(vg|SFQ?i00ryK25-I0(Yp z9!*MV`l#(M3Ax8}9D!I&DbJ>sFZ`Vw&T*Z5>m?u(!~yp_P@OE+TVTz-(;-DtD(q1$ z0tJ1R_k+!LD6-*2QZdDv^=@nGCeNB1kAC>6$jh%AX8Bph>&@nc#=$=uph@RFBYa6U zc$I$_;2GN;g^|t6OAJ~(rT7Ci0uC{;4D{mS0u&k_Ja_;YC9Ln?b6PKd_wxFHQeDPC z%|Mso->!=}bf;|uZo~{Z*dU3HmRLZrFDfdo{nG7)vW-MHfKY`YLw@LoSf0c)^Vvx{ zzZ-cRZvY1xm`Woy_Gv-6)xl+NXn5EK!BCXp?e6!|K{Pa}n~{3)$VrQk=VW_|kC$0O zQAn>tr+x+N6&l(L6*?{IDGw+8$dHi!XjuKx^Jkz(#P~1mH)qV(9&!iEIvbesB8Ka} z(lg*g(k8ja5!+DTIu~sZ4>z}uyVL8>HwGvmwWN8}w0O3hLqkk+TJ>OaNkK^=+#DFk z=AK_DtuZAdeumgQR0KmoMWre$>v&jvtfZtw&NDet7*kW0;+Yy3m*=hJs?#iJy(BZh z2a4Zsw%#lgZ*6aP(!U7xHyvttg9(mE!f6-Tt^k1LLd6_*#V1J0GSt}GXLYxV%}oF` zR4sCmO<@ibC%}Hq^2M|l{wYjsbgHz~x3U1lPyn)1{UE|QgYa@w8g8aOXzq>s0JV88 zo5HnGdqo1&ssLVd3))jo`reiq)m&_leT9JcNNG0N1k;!=%x9b4;i|4b;$XhCXT;m+ zR0R{{Z`K^5|H)~!6^WTKoUZ8LT>E#QP;z_i6m&G#%fPnaZ`e@OU3g1mB=$NtE2hu) z@9(jo*o1^`n}cbPeooxi0u9RD`ny*C*CKD}-T?zo;8(VxuK2e=+xFCtUEmmrn$N)hC0`U?QgocF;3HiOS+g(qL zNZ>KsD^}0|e#Cnld+gTNuOaHqCNm=&U+hg20GRe(GL)Ey&1Kc9D~uR3Y^c@uh27S6 zWJClHJSMcT3(cfuQl5x#hnUY}xz=cbq(J4Z|y{BsdQm{r$e9#6{CRX%*%ne zksRF{i46D{H*N!!}mewGWT9%Ux>A%4|do0jKFR1Fx9ul|y1)m+<5`su5g(Ko~u{+ruGMFB`uD9G&JTuQsYiN?Z`FftXf@$(F zjhd|B^Y~bdC~DLh8uJ&%mg?OTw>QLjS$R_vg%gbBIrl(?bEU#-K9BAnd00d|+)5H= zNLfn%Az}?A^4$}$qzT3!J(}M*1OU3l{mY#lolSZFek~l;CWI&A~u>jEHC#aXdl9C|2D%ubgl?MU>)8^)9OSNJK%(eA( zcFXylssXJj8K%AS^QoCiqjvzU--1s-6vB5>Gc!75z*D*)<@fRj!DD!Y1z4to+?jQB zEX#$w`e*CXUn+8TbGZfhB^qk|nLS(`Bdo5j4umT2$LHR!1uLzVj>c5z6Id3WqHSwv z*SMVqaoJ4<#ke9!Lj3(9>8q>y*(H)2A;3oLhIe+{V)7L+X8Y=3nNKhQTLT=vKixMq zp1rHOr#7a?Ai!dB0@}kdY92z;!ITJMlvGVSdQl-Z(EIF#M`lg|83T<(AX$3sz&+pC$;OhJwXoUly5W`JdDD=D2iwfYmZ@AtulaLO6GBV{!=z;o*{Tom(R6v%VRy;-hVDG{KJR&d=LWf}(Nm z`=ixXNF7G5wuO1V%Od((rl6=u#N*Ni7WrcBMvp`0bbt~pOFjhmh#E?$DzD<;u>C77 z?e;1jZQU>%&}3lK?$>pmm%Qeiivzc;*qCp|vQIn6Ik_*Dj}4`>Vmu_mF5Emu3sk}C zQ;?NMgYhINFpyERp-g|tE$?zXMLj4m#AF`O)>eJVi*VoI%#GKbb|`DNzaSj+9B4vT zNP1O$w`_2}=p_)a;(SLspD(_0^#{F;2;23Hmu|1s?{V}gc=F6V^b~BsrX6Y#`t2J@ zH*Qm4fXQgdgM>bhOFHw_64Kt`1OHs0`T6{wRnY!tL4Cvou3p92Ff%2 z0-U8D_F-##Qnv#R7oENl8mpu(IvXq2sfdIyKhe0=>KCd~aZE!b5@J1!V;V37Ok- zY@&6|m!UsQrD!~cQ(d=RT&=N-DZ2~xuIFd4H%}KzI{tpA%r}Y zi!oE>hEfI|@oH*Wnb}MU(Yx~EKJ*%_dg?uWJ!I)13`gYL`TZG08f#q`Wjr=%Gb6K!WhTl$r8ex&0sH_82lX3!clYrxhR$OSI#vebZS{96oc930HB z_;`HGuKBk*_!21#XrF(jF&73;jbzTc4&CvUQ&MJ>uu6j%KiyiHa85e@^3365+IwDz z%~Ci{rRf*|>XZD}89s$aM>}j!{`s}DgHv$pMtE5fY+oP^zs*&a9GD(2ZT8a;{QR@F zHqYE0RFP}Tv!xT?tC+T1W98VFST@@oegh@y7;WNuN7(M-=W3hNlmVUZI$WN0gglSF zh9x9aJFJYWSDuWHjY6U2MlxgL<2L&vSAbryKYO}jQ(MH)PqedZZ*S*uXX{;6lqg5`1sn@+pYBG9yEeQu=iVUGNVVHp5Y?u3!<- zjtcXsYlHv?7!eQ~+3XGkvu9^}D}dVM%@0y>8;vk|Fn?bts;J0gD5$H~l?B=qs=Kt2 zkMzZ3hAHLL+1X%M3mi~|Y9+gRe;(P_%8?Y*#N5&@shBv_WW)jx^hvRjS=p{EIGp4fxSm}@4Vli8B&jtXQ`|3QOK z;U{_A>W!vxqRmZJyAnJ<&Ioe9aM|7Fbcg|+0q7`+5eBHcpi>Iybo2&PcRD&cu);z@ zLgM1ofRSQCpNg{bnoX_2bl{HO&MVtBs%6<%wlGupI}3U$st(ZYinl;VGSnZ)IQ(AV zmpHVp1KZo^S8$I(?*&x`igalWhXbdEnF&U;*Ua0S0VALsOP0=IxiELY?RoPTHg&11 zKznAjyACH2{O?M%qg9D32(a}*EDw~vCMWNCHF|;`Wnj}3P<%T*Gc{dd77-IuoRiZt z>s=2!zD!KibiYwwULZy|S>SODmkA#?Ci9e)+n$`*#XWc$E_pXys%DzeI}F}gEh3^r zq{lD)ut-vB^oMw--U^t(t~$oCzw4WuSGXU$7*3aFAlrI-ndh{{bC}g#6~lV5xK3M6Wv2g${c;2-5>xAg_gv^;+PCnqN^H4bxS?y7>n-7y0yDl1*itVq*; zU;U}Abxx{3g``gu>%}P(?#!4O8`qrfk!t8jnYIJq=+5>)?0;5*2NGD0ck65R7_@pS zXp!{)VR=djuqlA1m%!4&DHM;eY70I9{j$T?H+~ z5)$D1`y?bJ0-(7pItjnN=dG{L@Swt%fbSm-IYqTesmN#-W;JDsW+w$N|4iDbX;i;( z!GOF9h=nWvLI3FAIi6hzvMSL9e!$=7CARELO#x|-zZf}oFbR*%?W|f#dq$o()CL~Z zRb>4Cl}JhEn|aQ^bb7UR0O~K3@2e){VqNW&+lAVuMpv%M>f-HB-DCpIf@c;kwLSo{ zfsK_As_(6^H8eK1j{tdw0p+8Ie`;+vx*OBReeoH~Ii((IDI_&XOG}K5p3q)Gcy_Fv!+rZ_`Gu)*6>2Hut} zmz}m|Acp`E4^;GdZO^WMelYM5V;5u_ZBSD@eBu{xEa-_)zfP=*^VyTxpc)M*TtdRYyx^+TAVMyP*p#TfX<&u*Nm7LPiEDc}0|6s+p#?nk{@ zF&*F9d-!Ir=yZ+_QOu?BIm`haJkOR&kBlr^e`^`#bX)xeG5=riy;FkftoZOkM#_3! z#|aBfhKadCJj_$u{njNbEvDD^^2StVYBn>N;uuq8;6F7Ow3mJ(P3~7fAsKC=MMaeZ zCUo?vTE4U9pI%x<9iwjNa}hn{zUHgS7T0HK@dy9ZY7i3jKevFiJD~6Z(kM2C`_+<0 z>EjDS5*0Miuotv{0u2Shz69gnNIjv-s>cKMtU7Uro_ z)p_DH#e(;O(%+7GzH5Uoz;Y-QfFN>HZdFfUd1GrrtcQ9q_qhlLzJk0E_%=&qeNg$~ zQ`T$F11d=*B=K{VM`~<4|MKD9q3u3(`!6nl-+!8#{NECN!4>>B-kCshLPVREmZnVf z@Ol$;1TElx1b$DQL&EAOa`_LCw&wdk3BLEEY7UwsWb7X>N!a5ze%UH`6N@1~pI~L7 zqHbvX`F6&?`w5T%fUix{c)l#mw^Xt`U{ysF$>6$p@}FbG^pHZ5eCY!^2dm8H6Ar+> z%cgvomR{lhFTQT4C302x)*7sc(?+pXhKOcXz8}>bO5008=f^&H)OP5 z#SKxB?<=PFVo@YF+3pA-^jL0HWl8T%y_c}Oob0sg!}+!o$Zf6aR%pcW^NWw!ne&S# z#fUpKn$mv;LJ8HtgTc?i8#n**@2+%ZoEp{hxhSG-(>x$BZ1iV@$ZIl)CISVDOl$Ns zn!1uo&Hagxl#6<~D!d96)ky~wRAHa7TAAp7Z`t1&3%`eLG*g zP{HKzKc}i>4qY33UrZ7vF9!84d=VF3quC$7h~8Vd!S;tFyOK=#_YxSAfBiN%ypd3#yb_8!z;6qQRxVB?#( zMLQFi<%=nQPR5ajs=uR?Y}_aB!47~{>yF&4*tR{j!_+Yj_-8sUb||hB#wSGKVx-Wj z9Wms5$`3iOCG>Gii||RN=Ff(Zs-XTOGAJ($DM<=@>@JZS(5WEX$Xb9Z@Z=4%ZXyiT z2$AAD->{ZZa|Gb0`^|)wJ`C=3a8Szp_aCq}>Q^t99iUns8mxErg5mT4ccG{LT5>#~ zXN~TuQ#Hc*h%kj?-4ML>aJ54D|J%JbDP(HfS-;&*>%~#P6mvdOpWkM0~Zr zTO6c%&FfLIkYol_GM@l`0+3wnQ32~!l8%s>I0an^6Q6#acK|O z=UiS5twdhKLb2GQ)JCS`Pfb!6=A~xtgo;YDyMwFMz{ER&@Y~i-U%^yHi}26fHWQe( zOi(lp)qRAMq?MFDrSpdT^k!{a<9E*1hE2ehcW1EpYr6$3ZK2i^N9hmdrX-;8cP^fpW8kPn=xO|=X z@npZoouz4MUijz~69f_%7fqNznv5%pjc3aao7`!hcp8^VIS`LdTQZ*ZZ z7s>SVxtoD~IpHFdd z65Z<4SgKj3hs(B&3x-T1>hFG?YSGD_R;n+w+i3GBo*Hq`QVmC~n)e=4^2d%kADw{sg%!o@%voK%o2Ua$D6oQIufxEV2jb~v|EOFxMC`}2c9 zKo5e5vv=(X_wcgmGI;kdJr1e$+w1zeLy-L=+k9?GY^D=TG`91Jp36$pF26C$eVs3NFh#~l z?4zVzu@IvFP#Ox9gZefwLtsl*u2QI;WhtT4tYT;w9dg#e!DDD(knoxSWHmI}LH_rg zGe@|=SZ1mIm*0(Vi9;PXQ|9Q#jhgRREk+NDikks!qmfXhu=~Bysn+rC%lfjTjSaKn z=H~@%u*{w`-d`h9DIi5tLiUf1EN{7}?xerhWd3B-#Jc`6+&A&yur}ec2;a6IbdQ}hPQxjGz{KHbt9USdofJX?KL zI)-SE#iy7iW@bhP2B3AWMAZK2tcB&oEnw{mrSM0nSLk1urpY-SO+J9Uas&#la;bkW z%VvEL+SVr#I@L_Mp2GGf?N*ApkMUivCsvrtzDuuw*?h`OPoG3_17~v{Zxc0j!ci^c zlGp1ji19!=JYwP+3McWkc9AXC51T|of@Lzk@GkVOtellH zu2lvb;>O|^RCi8hM=C`ZfM=B{Xd7>4!W4u{9}7A-ZH-%&jOV-(2YaTH^2#_?dlm2& zfeqKI7}>=4B0^g3bAMssQLbl1gPwy=00`FTQp0S2KPhasgg`<1Wxe&W%y|yMn=G+^ zuM!BjrnB?)sBu7}L8KId1w#!2;=eh#hc=uK_0ZN1PnGPHquQRH3{>Gpn8Q^v4@Zpp zcjp}!C!B3BU2BvT8V)jm=5ccrXA4b@XKpaW^T@pJQ+ z{j3t^20`2VdGO+ksa@j-{>?$Um=y z)wg)invw&x{caihs`3g8dl{CWFKk%fIrp5pB&asg4%!62X6HQELn9wNAacTR%JKG| zTW~!CI~4yKtD}mkiK2yT*v0bCD@PL4tBctNh_>C{f*v5>U6(p!m@5*#M2siZH#EQ6{u{|NPZZ&q3#E zBxu;I+=bKn(fg;MPRZtxNBCNz!J5=?luw_J^tW&&Y)9ES@hQRaaR>tvWk^Nx5FxJlH5YaUK9l)2;210 z>Y$BrUI{MGCsvNZP2Dt}i7JN)LtI#&eH@AHu0rE1!J=qte*Ptx4^X?ltLV}6R8&&& zE*b?Nuz`YtCg38>dzTj*8+ZV92USoIaCi5H`$k7sPvUGh7&Srd&DP#JI)A{hT%*#w z)Okh*CD^~m*-e`OJ`T`ptT3P@KHG(zyM5rgdLdU99wHd-ctFcZ^ct)m&wN^xX`@*y zO(7l_5#OZ64rh-;0e_~>*aK$EqDPkDbG-2?j8ihg^)XHAV&t{1=(`?7ohy!8*he-$ zLUn$6obm1u`+B42GV2T9i1Wa z;!F9I!YRtbKf}VuH`n_pptzfdY0Z_wxH*%v5n(hC5upfP;jNMS9Ki#%moOOIf8xc5 z|M5Wm-H(0!<5?|~x~0XOuNXr7>EL5}hVZeju9k-mU3*bj}kVgd}xQKlLH;iTq!9&W-*f>D@ zA@}ePwVcf*#iSso9L`Y+CAEL-Ug}Mebbk~0RHKN-|J=lHdt)SODux)>%}sGg{CkJU zQ3#ga3K-=8f9J*q-H8TEFabPe37pSvl%>UEbcYwpyee27Y;tN3#L{CPPsL?w~5H)&irz4tm8qJ${X zdyigcg3;T5^F8<8-@X5O|MPgBF~`{F?7h#~`(10j?^+J8_4QNOQ-q<1jmyImDlh!8 zma~ukW(`zn4I~;!bV-^+h8U;b*T{aYkSdTGG-^K?o17dOnOF_DJdi|A+ej1YPVTi3 zIVU+E(YYOz#vdu15h|dRWXcIs6m5Yv=+&&$G}x@Rk~r78V0h=2ltKuW@e5gp*QOF>l7(3#~=fF`H> zVp;6Vk4*fQ9b@T><5M~IIBuKaTT}SGKy1T0#Bc`#n?qU`Jb(01#A^$%0f1GX=6eCA zL#jM%iCjInvHk8oH5!UA$W*4ApK!(Eovrb$rUqsu0sfT;54^7RoZ$UG%)Ec6we0ze zfqYUtl|VO2n^II{dqw`h!dk}ouk`;L3*{ML9D~>YpEm!&%qgMAs*vcxsM6Q*2BRSw)6w#>YItW!iWDh-=w2n11Ys`~p8DeLCa?S^_j!xe*&9 z1&1*@K9?8gT2|y>jHmzD2w#oGzi4tvy3%~DOy4=Y95P7m30@;(+-t(-+nSXO54uyy zmz%tH^>wU#I8JCFbfy4xG{o5>IW?ImO`Bu4W=#%nr5>OBvk=lF_xT_7ut#3M%Nq5K zU>|PXs-ldMst8n=UnaO-n31E$o00s&`G1;A!&&Y}a|tdgHPP6cFOVe(_x5Hc1b?z7 zMoyKKv#sj}Eft&oW3JlYhkALn zfO2rA-Hfl3L@g(6^1gzAmn~J@J>a75)o-$=GEbPUr`-Yo5m-xr8id#!>oEQr>OTaO zO++eIIw!x48SD3*IZSb>@L|pApt_&yxqDX+h?Y&@IKo1{2gXZB~5v}+_W1E=*3P4EvS%eTK0xSHp3JIiP_#%Hb}&w z{6B>U2YrD+{t@_1!8RCpMU4;6!W3ufUD2M~C$G&SK)#a3#$YBkQSXyNcl;E6>2Np^ zE&MwV`-r;opH><>al!h!nOc|C{)oktWY4;Tjn&0XgW#F>6luoTr_<97l^AD89ZWpF zZxwsGUP%)8ok4g3upILF8h~AoN6zy+|C!ih&LtHKz}foP_M~$c1c`2AA7QqBms_lN z)u8jymx~2sh4v%mQeQ9G5b^+9ZjV=aF4oNfycbH&n;ewg8*go86d4hr-9@UHD%MK& zx1D~yg^dZOubD3Ei<~RVazRvN;>spjZf)iQ@aPun3!QE@2yXJ*8q0N!ZGwexYBml) zJiZbGHP!AXL$@c+?~_urot+(@b{R_CVIE%?yPB4?RYwc6j)zDDl*8E^*`I<(WAoPF z^7V1A$FlI|L|-4B=N&AFQN;u>G(mz_VPV}TL?Fhpw6a1(7~TQO!F6|bK4oTJ*gGbn z9exaMbZQ`5!ZbN~H&1K(7S8;9fFsp1?UsXGuY*;~A3+QwBb} z2^GLUIOgt{s)e$)Hh@B1T&$0>@M(51Abiz=~W@%ZTE5H+Hof3_QAUG?kNy?YoA{hdf1 zfGCArhyNBc_#=Q>ufeJ4Q5_mg(&06$m~f0s6>wRJ6XY(`tuZPS%N8Q*OLg9y$-uB3 z9i4>Jau0rg?mHax7)TGJxtJ%4qDy+&`;Z`Yin^YvgQ#9T*op-NU5FUVmp5JPps66R zu@EbpjZ43c4Xo9r@lLQ2|NC%Lop=I@`O#iWt#_oK1&0(8jj_pz`@L$0Qr@@IVR=r9>g?f%3Wd?m!^j>1pa|YyH~6 zo%6anSIu){2@V&(+l}@0v2XLKlb^QJ0S`{udAj@g-3c zpML5iyEO}9+BWY%_Lx_%7@Vl%$PM8!ATcVc1UynXJ@H8?DXoR^NlE;MgZBWV9)QAF zD<|OSqONm>t!>r49dM)xmzD&5pMqVmX#iPFrY_8@-r03nSu2k90Zu?1viNZCozv#b zUYCY_nle)rsgq`YUY>^6sya>{viYp0e#nV~`*D3q3-lFmTTGj)J2vB$S#=-f<>4ns*a2S>-tn^}ZL0?t`rI86^kD6* zLWYD^@$RXfGmMF-BVXd3-;3);M!EA#vofTEgxr-w=Aof*@plMw^hK&@!yzif#8ZGu z(d(sAS02y4GP8d;eaaimFPO1}1QCIBP7Y1D=uty;xBHdBs)*T|sF0PF z?KvGf!7El~G9o~|RuWB3^$_7bo}g?eC0*IV$`&;qO|B7wP1pMDHwVp7EdDepH*E8DM@Ka~pE9CN zW?gL2gM|~;l9kTKpFxg^qxf@j@&sMjmXV@@a8%<~Aid#`*tz#+SV$Z@OUHDhsf5*T zMChjG55_0$j8sEhQ2G0i6HoL7!KXa-C7IX4bfn@GtqNnIa1=nCHEe1x z!fAB0cWI20Tk(a&L6UZjadg#<`c(~yWei8FgnlEJA&RhrZza5Ba#9p1H> z``f#|b8>ZbhB?^|O$si2e{s;?#k-00zDY;WUZ`ExGdU^8!JmWTnZ;uR(vTlVWwd}y z5Q>L)yqdnUp{YIn`{Rw~7wp@!Z!h)C%&c^v&=-=+B+HP9uhFS)#(jg1tJ)O4DCTIo z%hIhl0%<(+xqy-J3{J!SV09wRCixj;eX61?6|vAp$nB7V`ir-+#nc@}6utQ&5Hwk=49J30x&Hhv!DPA??}=G*6{!9CF_FI2$HInTev z)JxncKleO9U0T>!SerANGj&pN(*z+9PG2Yye(hIE|>DzW#pw!^EJKzPnmq7tRUO(u~CixNJ1Xhusa>X}FwC zE_0ka2MXs5cS~sCG&6SQCJbO9u^LP^{r!d2z*gDZPz{AYa76A~f_(HRW!gAA9ndwEy9+7^<92jLAHBU&mEC8Pt7 z7$qA!yR1m8*Krqb_C2uPx$tlX&wPArGr6>v6Lq*Xxv@MU#Q}T8aXm-d+cqE>QgBe~ z@4s(xKqJyGP8(8EY!8s0lF=#N;m7nJy%$2MGnRWRz|*9`Bs~jzWOdPEFXVor06%u$ z4UST4H7hu@P(8sn*M=lDxvgnzid4^fZUJ!+5$BnAZw#-;d7O-%#wX^&AI@CMI2B5v`- zo@+k=ZI2&$v9On*RiZEckh;M1U4bSWH)=Y|SIds6F9=MDB-4Yd)d?W^vddfOX~fXP z#Kg=D5`1<5uQ(jgP*c9{Ogm-B2-)tkWe2n$zi0fmTcA&evY|}ZrvWN zaP|~q0_x7L#vCur!Qklh^o1EuNUdJ6+^Rg|~i9a`^*g zfhJrNWk(j-_khFb4HS6o+v`VG`W~J}+)0`ON9;E_IG>3!i)OdOeK7nM)PL zZ*SAD_laU-zj=Z=X|ff@4f$NXeNuhYIC@novgInTe-5m!a_of;fdH5!yvfcQ19-7!kF!uf#iUXp6#GU_ltU|Ctxcp zzwls=&JXzd)pKPL!om#1>b)I71!y_kztp#=A+~6NO01i@3 z@{+QsAR{9J?Ar$_J`I6o=;zTeeh&HWl^*bP)&p>Oil2+>9r$L^K&cbv!{h4gE+sWr z6F6P#A}nGviM!Ybr<@%bT3nPJ^76#yt}*Hf%m6p+hm$6jsP2~%uPj95yTik?c2=RV zjk`_nAp7;s&xERB{onlu%5ceg3_XTRJZ4ubzSK@XmXJ>Tb38)NZ7CP;J9lGZ;tKapzNRqiVha#nhr3E`NEKm6gfL1r;_ z*&$tXb68jup|X-M)Zw6D8;bwWO#Dq>U0q#HQ93cI!(Upsll7H!>0qaIn^)%7iktC0 zqjicFgl*S_Qu5gjaJiuye^Lv>=ISrMx$l|zh@H|B@j=5s`K!MrF`S4w1Ex}%nqPMB(9lr-)p?$?@eWW>EaUo-9?^p^9+cCg zf&33?=p zNF}Pv^|;=Fj<-RP)nHu$Xi{&aZ!+l2a=71X4E3S-JQP}? zy1t4d`4WADA;nYvLQvCz;T7%rwU>@ZKxnMLooy=N0B?lLFE2A#f+R=f)zwM9v&gJc z=$!n~{GDj|E2qevs7zJap%sC2Xr|NdrN$Tb6MW+BCST%y?PlS5;4&H4Iym~%Ix{zg zjPK`F1AYvd_{$uDUGOe;YG#(M2qzE+6yHRROb#9!+oydG2G2v!0s*w%XE9q$^?F_` z?t^Z5V)8a7wo)afx;jD6Bd);ArEv^P*jEwJ4G%7f}1qK$M6x+TeO ztHYTz7bC_Nx5lD>Q|LaqH|CA1#m^uYNz39lX7t16<#(#QS5YzIm*XSd#+_pZc5{gO z6-L)x%tV8!t_z2Om_OrH7yHLYcD>gIy;+oqQy3szLQb`jL4yR{!9TY|t;|h5SS~He zJaJR~lqAP?4I=tjfm4aaeYdS;I@yv^2)JfJm_A+zRNEOq1pYW!VOUTNuKF6Mt2xr$ z{1UD9uniRC2@I#IN7ekI{X!uOAb~{A^}Ti{ z_9UMNs58^2cOcwpUng(OaKCNMX4^;*@xZv9F{(9q-{e1U4Aoyx6Y+F#=v>_+<+@81 z^u?@K6-u1$HOj#<7Ix|OIo!AZvqqCu!xhKJuU)Z^xALWG_MG1xXCBV=wX#LH;+f0~ z%)z%beWb`rZ)&HHxqPRNF2d|wSmF-N=dia>cKycm{Cs9HGhdLZVwAc*zE?N_wwQj9 zPhe&{SN@py_-DII!2&VSB5@dJ3NTihpaS*-9}v>SQDO8ckJ6XLOS(Dhrhb`q#3y#8Sr$YrGsn}n&{)wP4`;}Virg0;b9X^}sxSC>1r})qX!L@aOU_#%Z-muLN#SU~OIS*dP1;Kup2dzjP^9{3(I= zPeQNh0?@mnbsW zldma;g8L-V=i)EIr%Rt5j9;>6!yDp#9QQw)_PD7<1X2qFeol5|Y?a)oR`mV1D)U30&x%eaZ8o~XFGCaAanzE8+GhtQ*gEI1-!G&CgD zRd(P=+|bZ33Gr7cn=Aiqa(qQGEQ=}4H%D#9F#~ORSC5mBiAm8S1{X&`MueSI9GnpV z{hIRftyz#GC?O%ihs&_=W!lf5Kc7JU1Ho|yb-mT}@p*5Ho3v%sYvi&vPwI<#Gd+-P-zk8bz6&J`MqZL3c1TqIExLWkFi|@NwU-9g8lT8?us$VfdYlim@W@>5l&i z491z0v2|WO+dQhdX$2&F)^=bC6Z~!H)M9Qocf7h!$!|?&zRO6vze#yt2t`bL`3ZG`&Y7_b!;w#n9##?%u}hY933Z6333uTwY2W; z;@-JOZU$(;BICcR&g1V!!Or&&&k(=E@QeO!gdOyRJst`LQv8EJJS~T)gT6HS?2*<7 z&VY*_vmI~PU8Ob9`Rk6u=0VD#^hfsdi=>*<{UZI3z(r~}#Gv$0{8dx%dsh_|6-ge8 z{znpwVEyk`AHJw)G_YmUo%D1Uz1(gA{*&EqtwCV+mczAl;*&gKdtA=!VA!zmD6A}g z)#YpSWJtVv_#`Us?NM$StfJga1bXVa?W2FL22kR?l&fM(&;379P+0U87oFg^aGqy2 zzP#FWdEU5XUk65>^XL8(D|7azxUPip-#IWODQ-+J3epWfeXh2;zHYhp05ttA7T}7| z-Rs@d3eWnV($@c-)?Fs2dIyK*3PV|%5TWi<_%^Fi!dlF@P9epqY>3aI@mJSBPomyU zF|&f)D|0p)+Aji3q1N&^b2Hd*%th6I3M3bEgrXOvi?*9{0o-fowQhM z!O1W9CCpv*RSvQ{RTW*gc?dN~S4c8xo2ndw3)9a+!^t-2WdHegCLaJmRSx{5pKrPL z#?l~hdg(2xjMvvWtk(5vrthuPsD;OWGO(^#s?SoGYHh}xWbBqR3lbhz@xm%d$XUg& zT6vZ`SrW)cnjl1QSvm5Zb9tdY3s%gT^6D=&LwUwDeQV>0yi_h8KUv>7Ly9Kf{#aWM z9IO2)kRVa@DPfUs6Q0#ordmIS0>!=uL=j~_hZ}6Mm(4*UQ}BY?y39=Vf*fQvhUwFb zX7rV$qg6cote#FZd-O{V6V-^|h?LuK%cR{xEUu+AI9jF*aeoOGwMlp- zOnGDK!1)g7c8icK73sab-BUsE=w>qxUOc0o7IKk*%Yqxec1k##N0* zs0960a$jX(m;5waNf9*~ErcIK`gl*v0E&+f0$nUhYZCK$Y~S;#Fi)QYw9p{8vFwDi*OkTh66!6<_vp5$`;@4y=5sGUPdgQjn3pQ2 z%D@)TEIe@;98Q?=I7kpY#m!UiV%>X#FcTeOVr5RG;G3G_f42}_nx1a75*-k7xk@YG zXL(ufxP26!b997V^k$!hQq3K!qSj~()#Q0{h)W|7O`bs$%j^`~v%(iON6{`1( z%TwgVJUk>VO9lcAQbDoV00o2!Aasvuw?zO@IVb^G8-B)*_PjrT+dA^H{ge{|Y87{%h}??c8H30-ht`+CQ6ZvZf!p3ng+H z#6pafq=$OL(%)~xbayLom~|P&QoSL*TyYZ8AE zdAGmV!{?_P8m4Nv;}y|%(3ig}T*iGGMGK3HKHZ}M3YKE17VUSp#rHpv87H36EJ-K?yGz~zW`NO?9oP~n&JU2PGwm&%?av0LNC<^ z<>gW%zdQ(L3JaPoG|~^I38hJqTI^IqbneYvqA>Z!dMy;ldU^>RABXy9C8I1tA79=e+&T7wM0T4nOOrVMsO5aDFoC@GU#Vlf zq^*qUj3JTgGQga_pG)wz@Lz$$OBX|O1J*aJaov~W)xkjzE}=2)SJMJA*ekCOJTypA zU^dRTTM>>swYenTpUQHyyt-%(HDqBRDfyWE8wr*B(hy>5Zz>wb3st?}7fJ)q#A=2k z;ieGX;`udOQZcwM`r;-+Pf?OaU#NpdC^E*oq^?<(Q59J6Fm&*|BLbSLpHFE99J8Gt z%Wwi9baZN-<7a1oO_B0VT#9Bo#3m|iPgQg@$Jbhq*_SVudw4Bxn(nU+uwBR)L7_fv z7ycqNCAwOD6ws@E<$Je!juC&sn@BD8l<5gYgp;FrJy=vU%QEePc^@5qrl!@XB^=23 zj348uCOwcKu^^SDYOS`&rTA+$P}6{*$gbsRz#)*><2Hsd-QB4*g^K!9C>8a65dj8v zsCQ|S^(iMY1Nz_n9U5d6bKehV-F>5BS|#(}o^KmUNW5yJ7D_MYlUQWsP4Kvw{!^Sv zL#vdyG9~Ob_ns+qC#EEh7rm!#t0PR{Vc&)lCG2iASm@6;#pAfLW#>&7YF#n8KP;)q zZ23sVbOj%AtuS*;Df>KS~Bp6d+oItc*;o<(S8pRUwIXHe)b20)9{c~dC>|vohroy^i>CF zY9IDAg2L@c^D->x=<@AFoOb3MzI z5f*v#hLx3l9c+%M6+ScD88~g7lZI-%uGg{+*z!E&2LE_emRk%dywlOs(aifgFs8^? zT|8=wsUf`v02EO=#mjET_iAHretX}l11RQxaa%Ey*NP3SdHUzTX8>N`D2My6-m&Ul z{68N^((zF=G>%J6wOf1eEpHEl;c>pAvnb2IuKdLAeaX^`;grDAh*d6aGENtis4Uo)#lpYqA-OC5@U&g*V-<<#o9slKl1O?;X zZ=>BAXs+!tA2xziPrX0ta*`D%!Wo}H)=4aja_8c-BGx2bZJklC20iw^U)?P1?&+J? z@&YWps9s)7&$`+z28Kik2@#=e63d~iX*m$-ld5%l5)>jSBKJ5P7xZ?L<@jUg=B{**kiK+Tpb7%&IBGe5y z@l34;C9(~zo6V<|q_;dQSk$1Wv1Ysk@uXzdmeyJtPh~?x$OY^^H0~9J_OQeWxuVnN zBCbaB^+%e6>b-JaUGn~wb63p^eA0ryCh={}>A8G(YkC&nbrzEcD?AWNp^xhx!te0F zK0Lhlw~#lJA|QdC58XV+KdZJ|?pb&G)Ig1JK*lPLVxBiq%Qq$QJ3k|gAtftT0VTD;7F@*jS>MSWDb!c7-0EidrsI~=Cb4!p1DvkljEMff`s5! zA9r>S4HcDG?%YZVnOs5sur${|YHYO4@tl^z_sZIbe8``nn`6BxBx0rL_L7zEOw}0o ztI>htxv;su{vyB!NqvewKA9gaN#G${YtXBG;ZR1*X;5-Ebi7&tI?)RqZI2bISur6a zjn?Y4dbs`SWTK@byx}kZx&u6BH`YdX_VeSkz14OOJrTE-%p>v3TF?D*(7}wY!XLh8 zQj&z$z?-fi@48JghxByrvx9sdFU|h5lM&E_E-fwBRH6g@n6)Q3@))Cm=%jpAkd$Ch zFrnx&G%q@FIc}C1ioI}rygnX~xwt_`PS{l3+vs529J`I2qpjY{p`|r7=RDUMR1D}z z6yB}q<4BchbY_+^g@XS1X-Vi?TPW#rJ2eh`SSrjsrPrp9v>!rk*T>9NCn$4tl79|C z1|4&eQE-O_yf|2hG{Rq<5bh_yf$P=L@fA$iScdZnzg4eder>pFLSxG9diNi2iWQP)^+uc7vJeOvdF~gkJBb z?X|_R>CuBCsCN((`nNw>FD~d-|HE}d(aI=9r^O*DBW>)NyXe@AoC%8+23Ih>ca;CNdd^ChVnx8c!2=U3sQhjJ{q^7JBU*=%yrB5Y3)(T*SVAH0w}79{9S z6(LMWB94fNASBd~eInV$?S7D)-mVz?{AlZtI6#k3z-YLhIHxo)lPEZXGdw6>>H%}M zSCfd*%!WpVSG!)D0yI~CNNZKg=Rs!o*NP%Z@7}OS zc^|@-Ha;$m-0d_#Pt!%o^)XPHg+XP7HFWg6Ada+g-QF2{x;jE5$k{SA52KdF)5CEZ zX|l1s&JH;o9L0`~yl}F_goD#}l`qroo6piijK#$HS0C34B<*LGeoLr&jXGb|&9k6d zpRC#p$TX%uK4vt|_~I@S$4)b{oX2$q)xWcRE2g(ElWZ-YZ~6SHi8t`o*lss2bEc53 zh$Ay!`;XS4hT{s`3O3tqz1dv>lGClnB7yvRJ_&-n>(AT%tivZNZL+e|W=hGge%o(t zbHEjIJG5ybgiwgtW)sGpHaXBbBJ>~&56{huI@48J8>wk08$-Im!g zwErqMJ}V(E5EuW|drxR8!(pj3kK2|yrIDP>5#o4x#L-k;ZLh7L%Di{wer@;*1QrUs zu~jlm=e#q@#Vc>m<@zSANIL7BwarHR=G4ACtNbi_;xXaR--W&`^^&3{uDXgq^#3`q z+NR>jUnhS5DM!1P@iI~Z_ma)!Ap3?$z+1T6?WPG)0=`8 zCl^L_|48-zYm*3jBgU5-De6H=nK*di>&DLsd zR9)}ss=6FANmpNTOavmkIaY(dN}=d6C*MNjy2-MUxna*Dpzd;#^0tLa`@|m&ES_q& zw>N0oPI;f4t5%F9k_*vYH+HqXiN~=wnHj2R>-C)0_^rGXXqS_>U&aYgIPl7ne5%sE z*x{m7`FMWMUC?n*>qb%eVtH&Y#mPd=+~(J7x!Q0vXd_JDHxKj2t=)eP)!o?Ox_D5P zRI{0QUGe3)L{?zYS|J--jKQv(#~b5oxY7T1q;d8f6Sb51h>niEB8sBBxp`o&Pm4K* zaG>tB?ndGxaCunIZvyxEiKJXU&EC~VEDKiP%r~g>cpdJ3{h5cfWJTKP+NC^9`(N=( z5LfI?oUGKzfx?&@!4R_VNzj9u5)mCIJReBDa_KUO&w*37qSM1b{wIB+Ij YEJvCFouLIHRiIs!Qj{!y`S!#A0Gf+m3;+NC diff --git a/docs/cloud-init.png b/docs/cloud-init.png deleted file mode 100644 index 63de284acc231eb37630aa44cfe8c55d0fa2d9e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37106 zcmd43WmH^C&^C$%3o^L7Ly*DUJvaoHFnD0F2^InbcXxLSl0a|?!FAB!8f0(?8k}!( z&UxQ^zd!f)WvyAWr+4k@?&{uEPe0X7q?U#fHYPbH0s;cIin6>80s`V7{8NdJ3NN`q zddz@-AwqPNWD#n|Dfi*mMh<$0?+w+}M6F!FT$a`@AR8`kuq#{&0YThb6n+V|d2dPU z4R&&dhsYYB4kcrQUu|F@$5{rk6_?;ULapPHN@|8)!ALGHhIxPe?e z-2bZ^{;2rhQc+EJ2OD_JfAvcO#s87~zsmlrN1Xd_<^Qk7{M*xiO5t6V#1!ZLU$aSK zs`aoKAs|R2sL0Fcc_SWmVx*Y8Nqr7@SF39CdF+CcHbh!BLj}PG9X$dalYd&U<{>1% zEyd#5@F3q);K@F1=^)5_X|+B9^#wL7y0VirIvonSE;>LKUm4vxZ^*FH=jL>xGH0YF zTd3=Pt^LpJ8VC1jr|*PB@1Ksg_4YrDb}m+Wgop~#cv>j`pI?ESvKybPYTKG>YirdN zXDhk;r2~-fC??mXPyCVHzpT>TWZ^&QVsIk?wu%&@{r~J0$ES^F^09PK&E~NoMSY?N zSfZGJJnJvx{`l+Vj_>smTe@XRilX-`g>?_6fuej6RRMhgdI$odQq1$U%QGs_Slj)* za>$WcB+#0mXjL19FtAAe$LknAyYVCH;v_2}!e6%qeWZDGrmhfKS;Z;f;TKEBc-lD2RQ9=}FO6*~*7|OAgb%3_pJ7$Uee7cUCR-PD z$xyW38-frrPv6)4WjKTCUUaOf6D%8(TUJ=A8&MyPMW=CV_q?^5Xs#kx{@8Ig6D#?N zh1XME35eCz(@?5DJDA&wl4eFPc^%VGxSfG9jc8ijM~V<(tgwMD?G#%?bRs@!DY-an zDR_Fqb59Wp2*qm`=)*EOPQVlmVWV4v&Eb4AI9P3HUyUKta~{BD1Ipc{XT4LaJ-|q$ z>-k{x3Wb+W@j%->Ce$`Ao(%{ieOn{RfMOilay>%6iAvWWuVB*mF{*5jzdMFK)-91? z5eGPb0_Ff>Lu+gYpLF;vl{04<5BpY(yNgPr=o6TbGVvLp>>{=p{%z?Tz_MsV?-4m_ z?v8bkMz)US-7Mh*l%l?;&~|wZ=VA(Hiug~%OUvO8^h=iOl&-Er4g|-5 zU8=RmZDZyg*O6bjcikT8_MfkW@Ay-)*m_6D(1ES`ug%z_q|4B)wz$xO@p zG7#g@g9MZ>axU3N3!h4dA>X}El@qH@It&q04;H?EJFxm@-CLUFk|p^3_llJKgeFc$vYq4oW?f;#*z!v6*}|vU_sh*oYv&KvE|CQjc4SmKB7e*l4(Y zGg#7>Vf6`ODv|YbUB6*qw!)C(eyd6yhTK}Hq%b+h1KEtl>^>!?e@DPq5dh|y@}?D1 zt4x;vW(5fd1(*r}soN4x0qX!Zp;lk2Iu7Me>3ggOTsu6&aU|G;^v5aVZ{y4wTcJ*b z$u8fjBR{huMFP;1NF9`hfp|+sdAgu6{2u_*@75mC0@%z4m?(EEzMQbzR8BV){;8{5 z$?#!<-RwVtML^Gm8lwH{z$-arr-UQzkOh2<-71duh-uA&wZlpBsUMZ;L!wUipIQvS z#JW}0p_?nkMd*F9wDzD9Y^Fg&Wi(f{NFV~>rD;Do@USM$;Ir(al7)+gYFXu6dbInV z1)6ohuc+9XIk^;x@J!`Wr^X*@QM&5%bZ-&v8HJ+K+Dw|l7#+XJ9>@h(Mpl=6}R$2^CMY~Jqr;;Kg)qk3GW$@Q-7ht$}>An3q zQOHxGi=*fcHVnr_J-{Zz#z`Ds;wdeY60DGjYeQ}m{)oX3T5GpB`?oJPw_7ZFOS)*l z={3yL0nZ`zd9RQ_=lyQ>t& z8zKP)nF~LCU=WX2jls(k*PNeHtv9l})MGN%%*>Qz7N5wC0ms`rdi&Y7nGY)Z_v}#d z)sH1fQd|Rl?}jmyn9=b!DUDbt?sr7LxPDBb{6dtJ;=R>maXEoeI;B{(BZ~C$t5lNU z;wGYum8T7*Z{Hc)OkGRUrGzp1b~34q;xI6JJsjVPcq8;^x)dKVJlnacNBtqkYG<4V zKhSG|rUh(az~k01^!|lTJ;kj}uN1RZ7u&1}jblgxaTaX|W*~k{4((<~%N#XHk~Of7 ztSOqI;bm(ah4DKKBK4_SlaZx;oXEwfWQ~WaExpg-43W~`Bk>YaDES^LS%J3brs&)z zUEfwazR}%#C_2MJk{FED?By4V3yp$4(EMoU=VR}FAJv3**526dy;UVD{|UhBW)hh% zYFsPfT%BPRx=pLxVW1Kkx@45w$FJ(!D4S4xI8rS*>{a`y$FFpt&E}7SDHBsbPQ9!d z0yB&R$|k3lRAnIK2b>PIRH|@A``pD8_k0`Q6Giu3cj@LSRrvl2uk01=9dO;!8KTxweI9<+NC*&?CIT_?PSX1aZK7a#8CT zGZT7VGP}yJx}Wn38{Z75CEQe;MlyeYb1p0EWFH5x)pZQVHfffrcPZ#!YN<}DEM|Oh zPqfss%1eJGbH1H#?FZ?T?u@kIE!S=-OXx$F{B`e$b5_j;4D)v9-CH`uZw%#pv4}~X znV-v5$t=qXK4#J%9kHE}Qi-RV*F)hX7^D(>2zcd-f0ke)hPoYA*{G5exExBV6xqDb zKMa$>dehKt-P43iVRAJIQHxGs$g*mW(L4nWPe!lZ$`UX{u!)r2v}=RZ@G2q*t=hXy zXv)0@HF=oTf=I?H8ul~s2|0$?yiu@YBlztTGBHh9J&z7kYKY9@1S1au_jm&HXpJwW zee7v3&?ugO7iq;4N3iez@bp8NMR zC!U7_qJ(_*_|%($fzU?*{Er?f!qd`moISP~3X3zs(#pv-v~UDFPtZcC`?=U#8UGj$ zDlD+d=lZ^}hmsaR46?RYjZNzx)uE&W<;GiE^&K7$Jypb6h|2BpCIu zFR7StZ$vIL=j*D+9kn@1k9D@&7hPj0+ zN{@2Pr=o4rt@6S?Qsk5f8c^>Iq#oQ`#7%J?VL-&W?4@cJVx;Z&wh*q)5BPLluwcZWjm6~G2h44Wz1nSKm@}4Z) z!!lJUb&nR{2|^kEy&QJVzpfhNgCsfu{W|dc}{ zB`rQvN>$-rPyI1ZGy2rx6naeVwEUx*T!C)&r}Wg?=MX9i<30lB33?g#F)bqq<($Rz z8w_oH0|68D_`|9a^Ynd&3At$)uQctuz9GQNw@$+dinG+&g@H)%s+ zpX|-GiJpD{ZLVuh&X30a2&?W?27Qc?3#FX8yvaNM{@ONJWS`qHqf&8s_)kk?XD_;Z zBI&;07e*zffnKSV3P05}h2Trwi4KYtQ3krrqF44t-cd;gb+pXND@Hc)HVHxL2MPYx zmy=fmv~ExZGi@ftuX`Tb6@3}eQh^V%w|{YNZ2Z!u=;GILW%?DT7knEIy2Y*Ido;eK z3B%|{LLP?!PZ!#EIE#Pq>S>npJCmno_liS&%KTz^fxIwtg*eri%%`zl2o-aW&|G8I z3l{;Jy2^l^GQmTlOt8yhgXHPWUM=6U)ty_u_ICXEA{bU`mPymgO)-i`+543B{cY6DBaroj4 zC*f&MHXHT!Vnn6~NW`Bqve-t4t;=A1G33{6Xmz4;-n@$6YLH2xZ*n7?z+xhIyBsG% z1_pij@>~B6C}xeuu!B08WfG%sXp&Yl#nqX{*)g6hXDLRofti&Lc#Ud8?ApU+tw3%Tj zs__oResUQhw*~|)p2Za7SLn;|>wFazt!dsXu`Ct*m>pn7Jaec;*KfsS!H`$FXCoF$ znLXb;0(;b0jM4_h(HK}3uL3AMM)N!`)&?}beVH;;cV1FH7f)M4OF!&b4K7WYI9 z7ICOt45&?i>tHv*oP5jXgQbCZ3|nmP-1R`#QPHnkfH2ufc|*I)Y<$R+KHWh`#czG8 zHwMhNq+(;AIqgHt;$FLwQe4w1jL%aqwe_qGLEB$LzlV8NdBjx{#Lvt9PESk8n)o~=0qPu|S(3-Wmn#A9jCU3cE#yLX8mX_ETgDt+ki3k=;m z7s7BH6W#_JaaDSOg?HkEZHHqU$cH&f7=^g8=F_*U$! z3hgl$hS(s&74K-T+%x46+FI4Ao8LF$AM{$UMy_sQls<`+7!kXmx!gV4w4h>Z2}r{w zNsT^}I65I8Fvwl|aV3Sc_aZ!bH1F#?5WSnxJ1``Ct5XPLuZ{_H{*^b<sYvIv^#7cg778kz5t<{5EIw$wE;CO~8+(D&Ukel+iwc7K!`1iK zabC_jizC3|k-qAMQ9VT(dwV++u?*ghpYLpi4+)b(_B2D^e`?gq31c2=!ba~fl!)+~ z!HfLT8A_TNGn5RsJL6Ls(($(Ry7}OHj$Pa3>^U-mU&v(jQgxq$-t@01Hmm%^ji}Qs zb;3w|Od+u5hGocn1~T_l0d_wS0E1kv<2OPxO69=pKw`iHl3;8mCj~f2{V2d_j{g1+ z=8mn7^luP9woH;^;xWnX93`Cs{l!LP6snltk!37!-7gX`mRdAK+J!+ejLHZ;-;Y)U znioG*<40e`e-X?SFsav!Fva4dqx1-It(J9qO@uEbU|-~3BCOSes6onJ!kXw4mpIRg z!8)lFk#)iTOS(V^pJ7O8y83e;X`ED+>~&CQT>Ig-Lz$}In?0$VwikK4d zgq|y@seQGUMJn7H?c>F+g_gloN4|RdvIxhqwA-mk6zs$zpe2}sNLQIsOB;R?1zfmcT%aP;{)-V-eo2xc)L&+J#vW5KjpAvc#}>#+VvgEB&3Y@hc~c%4Cv! z;A!1S*)&0*DzI|t6_up@_E$3IMWFW}*1KX9O7@vlsidF!N@YVJKeuWCgS>K9mD60D z@3mgni%4k~7R9eISa7wdw=?nnW(h{Tb=xeyM6F4_6`TzRaMS;~ukKqAU`eF?*Dn&aDRF&g zHy)*Jl@U`1;8S`lbNsxjT7lv(;M<@rc8^$FjK#GNJoBQK4rIGkFhgRXk0# z2@F9*WExWoQs6We>0}q{AUUbM$Y*x1-}+8Nu=pV>=04{{n)@7#gXy9M^}l>o=4P;^LSM+p)ZJgERp> z9anSieBQrl`MgdjAxCwC)Lbb8<y>R2xGzCHH@vZKXOAQFokN~Jl6W7Pf z$5pk?1sVD#(e9kJj@7ZY%T33&;Q-@IdGg&~pSJcf=R1G*fG)CqPX+9Yb<5NQsdA}o z*ItlhJU!l*o0#>SX9SXTwJeBVPWH;4WRC#{DZ$^bzo--v63FzRso{f9+$8YJm#PW{LJU8aHkIiL1Cdclb3USClUs*j zk>yM++3-?U$-kr&w~i22!REP=O_AG%to{(y)utO}QON4Av;Wnz3#EC!Vfp5DY7RMR z%?A>&s5fk9RG)mzH2{s_?05VfWKXzpIgmQZ5F;=M-X#kUZ%pzVjeWL*<~rV8A5*k6 z=^Gf>N!~9`L?CO2FLu~`6FI0Th7OnKtke`1iPWdvb)(U|_n|(OdMiv!tPL9TXFgYi zPuWGyw7CNGOIsA4Y>jNs7&6iK5hC8d3-(zKV?62~`yC)MpkO<84IQh&7`zK`asnuz z{gIIb{9@7N0M62(PIV-}o5qt|+89EC*hKdBq<1?k8Fjt25F*!a6l_pto?5M5l&K}e zJ-2SU`sU4>`q={ZUxLVYJpw;_J%r=>mXT!0M%=Mw^9e+77?hd%elTc7yTn zVnm3bac+G*$=5%kfRi+me8ls(1Dmjr!%CJkE$6RCQahqcv9rYUZh3c^zC6FTMm!>a zN6&he4W^LtDVz))y8nG8m4a%OqFy38$Jp~aaWAx37B9AWR4snsQLsv46Hyr?keElBIfOU@xEZ4~4(T;F`D^!NY__b#|w|(qHq8+*>;oIkC z*Cn#xIw`3oo%D8KZKyjKHG8U93B$JdxUI;7q2y>hSL`^A0+xn=y}=_rC-!)uWW?jW zd+b$RoRIExEB@iqp3h?>g*GhzD-P2@$nO$A-txaok?W6pFYx3(WlIWed9C>@cibH~ z!a7q=Uq*rjS4!wGoJ;)fF6+vE=rTqg2n2Y=uI8tY@JuPw=4(pc_Pt7Nvq+an1W+Ek zI?Za?s*0xZ`q4x}D4IO{ho(0=IkL!1z_n#l~Gq#np zg$o{bhFSXgF2Pkqez&hJqEzCXybRCQeCtntGnsg94XCG4+T+&XO_Q_Wq8x*%U^ALh z$8#$>WWLqBm)Q8v;?fK*pPd7~swzJ&+T{xz&eePic_AWO0+5_% z5T`8g)5zl=&SAf2oDjaidj0Wo5#yrm!_~Ws1uIQ-8RxIK1QrjQB+$$K!RsGs=Raf7 zj)T>q)9K%9G4C~UFJNW~kiVD((U;c3{qKtsI2Nyu4?>}px;~Y7ez<@Qzj!%zzB3w% z;%PE1E9Sq-@vB9B-zni9YgOF#ygZm__NbP`UIVq7-i4~GM_zq=eInAJ;_EP6Pyj; zu{??8@4f=`=!88_$=;4!KKo2noN^io0*_n25U|OoSyOWku8REJBq=N@^H1)6c05dv z+CC1_EVGL3=J`4|Ghpo+)O=VMzuiWP#@KR&>8|Zd^Fjg)rtzwJAL&Fh_iKh_L81rvT11uyr8<^|%H3+`n zn@mAPJabNqJqfrSO@_q>+$As4!i*0XR7ZhIG*-<#uDWY&9@##c4b1|w3EUVeGRCQ? zZ(tYc=AcG~dD1?a+yq(pyVy*;Gw!?IX>+?mckCVdK|d-?{2BKmwBebVc}&8ys`ver zIgkclD3up5aCdcB(=_^r7A43A%ighLM(PVcI>za8^L$n=FtCU$j*3rTxjlaNCQH73 zxydhkwdFJFN5&1=Ae{`M=WT&CTlbjAjb3f&XJT}Z!SI#(i zWn9q?sZLe!?8>~049Vs_H2u=`)aJ9v9e@{fyO6~?A+k$J*ZI1}A>Ko6#Im;JQEDu_ zV4eT;`+cpwhNdy!UZ!1+e+YAO#j4)K6b$l`BugQh*r?_u--%8Bb4Tl5(Mu@f;$b)$ z8NSt+<9scjro>f)&%u8AE2FX{MZnJ9{JI$=R^;i1%rh6B5-(b2T4Gr`DUN?yd>3lJ z5>3*@%Dlhm>9pVBX|ttfZd%j;nPW1PQHvU8vyN`(8*z8A~>Vhv!r#Ei%M?x%o6f9rm(c{HCxI zb+hwe)S}5vWhx?Pm{8pVj>i?rlO3Bf-3G|Ff}F~4e7MSYIa9T(I+Cx>$Fk4MJj z=3ZiUuq$f5*7V?qxe4Fc0bphR!T|USh;bUySS3+-EQ|2!2f{pgRhq;~S(u079k0*1HKaO&5Lh}u zFw%b@v_rP*_w1Jsy^07sPCxkk4$@C#!C#*5Z`>pJFwb<4BU7JhRp9+ow&3cjUcWUU zpXtGRp7^{2-|A^{;xET{A!M|q(*pR^(X#*NaCzcHz?zB&ozCFbKy0=X3`bll5}nWU zD5HfO+fSO0g4>rWlGKXeWwh={T&~y=RFWEKGd&-SMlpsn%#0MM%WVQ*Z19aW#MTrE zJSlX%9@fH-ro6vCcI?zyEDAz%)shWx`@XL`Gnm9c&S1HpRD*^#>(6`z&ng8O!{)93 zVbYMl7%0*p=GaaW(lM(=*}KZ1!$uzQtC9u84oaMr=@SbcFB9Dxb#RipISA;Bx#nI3 z@8rA1xR5!9IZ?Rx9ad4`wF3 zmoYX$!h;`=9p_CrMf91RLNi1zQ0@^nm!jXm)3Gx1=_)ijt%{LAqLd)+WW2v?LMc3B zt4n~il{{asLs+A*n!AjByx9^cy)|C+%(?VQ9%<2%xa5`4V-zO;(&v*W@5oQ0`w-?J z;wa|mIpu7p`5#z0l^ft_l3E7S0Ex?G2k%8dTi2TD$9SPIrzIWG7;5KHEvNb)ccy{& zcr)kyQ3%-st(IE|-{k0L-`Zl!vU70sVc50A@*^kWk)$4@o(%xM@%dOu%6Tmp{oKCw)0_4-F3odruY`&hW45?u{5~JiWVl)+#cL?O6do8ZM zo`Qht^v4xE9j}M7na6B79VdFgT9vwKb9Xs2v4F6h;os@^y7YTR8ln=f$J;jBC-gmy z2jj0-n2Qa=061dz{+>6|SaM+%^3 z)xl#L(9;^x%q;l}dGjMD^^3xPz}MJLuV&5a*Mst+5s}-A+W7hT zvvP~xNDG#5i>iaZ)YG_?IGuzu{yAE7%uB^0RqT-5%@G;Q@t8BMN*vFGltJH}wIq;g zThp#{$|~|H6jQQmTP-b%jWM^fb+H>WS)V8A3R!VDo9lU zV!K8mkDO1t{3hwI+qkin;`*EaaH(%l`X!4{gIjZWQ1wL+P6xZd*WotDZlTyiow{m~ z2_%ftB5JYrz8P{NimWXV`Q&8|<1N0VsB^%x#Z6F(T29Q$s}>@lU^)N zavN#I6U51t^s$HjOn>ELG>1KJ$lHrD;xo`Im@wro$0Gb^dsw#`5}!_3 z5r5jJTk29>m-}I4IBEI(O}2simxSV~FUC#&Z_TM04j~z|7)c~_QuG#h~zCUJZVSyG*PUe-H$$kSRekmia4T=d@ z`xQS*cumMOkmtQ8^bf7+9th_{xwN3)$CEWBGgTRBVw6%yCEo5S{d+eAemCwD_SYq6 zICjGVys1(Bhs+d4X9W%*GFykYLqFp;RlcFO>oX0;GRkihgKr})K$j;E^gU@+Li#)r zb4eQ2crjkRq#g!fptfcI=Qb-+s~iFv9n-+dFA6>Asx6#+`T600wl)V=FcfT)UXlHL z;`qBKxd0s{?o*Q0M#8($|DM%Wk{$*g@Krn-{JR|*of460@CV&2=0A+A7D~}y`JZ-L z_g~-}iQ&%xgppL^0_8pvvHz=2o(e8sF;Q@B@wY2*y4DpXg%UYjk3;WYeG#H?dF_0e z<1TpRe>W~~ptFjg;#BHa{HMua{ip8$F0bf^+W7om zg9HC|NVvqf@IQS&;qozeSnr%OiWD7mu3|I8OR7;q|P|X!^1-;oZEHsp3tAGHFNYl zUP=WE__RKSOFa~3ih0%gT+JF>Oy>#y-SQnJor1?2@4nl3TFKxFFVCiv3(MGw6XUvy zZYcT3a-D>M9m!k1r=50boF=w#cF_?A&9+VY~PG! zq?>=J%miA6-G&F;ph0$04YKOsBr=@&Xdd>>+``YGG?MCDiJH`m^Le3DKGqZZvNQa6 z7hb`Wjok-(An$hGxK> zE}!GvE0aHyg>umJ@0U!ycZCE?Mc_OCQO@R!LEeCUp? zTXpTlmbgXSnY-nSqgf{~ekke+a((6;31AbqjSl3PxF&!kM@5zuj5w zw;yc8PyrI(oo@u=8q<5cpwk@KnKOpd0?ro`&kqxxD-&>*y!+_DEFLBO)c;TW^$q+a z!jZW4AE?m!(dP!kN#-qy-5iLM)L=Y~%iu$m#pCC8N*eoYASGlBczr{lDQ-C~b}G(i zGeYS5P}_b|e+sA|;IVUxX>My;P8eZ`7srRZN*L^VzVFHwfx!C2 z^GAalMzQlLF1)^9fL5G3pRedWVp%{9+%B|)|S!xoKKv-)Ggb=SjaSIa2W;QKjIbX7C>cYzB$ zHB!T=v`S*`UDyh@SKAk>9795L?_V!oH=UqlNZxKIuUbI{ugv~&zov8vB+#iZ;9ImU zA-TJW-Oc;rc*%E9r_YaFMtBU5LFkmg^T*9-Zdi?P6?ztR98U+W;2+swSq@q6aKOsw zFGM3@P)J5g+6TZFpOn+}2bhp=!XQ(+X^&<5cSot4C%Cc7W*>O^?+%SSyz6G4Xd&|! zYjlfcWz1DC+73wD8MM5P{*KKfI-RJ`USW83*`Dj$vGQC`_4w{j=?-JHkmYmm`!uo^xLsOJ8Rl@{+DpE0wc9v0R?YVep~LGr;zE+#!;(k1?{~kxrbn;7SBMOs#g- z1?N`BMZbQEpPB1;ywr!Ua=3lFj0rf=Y$x(ow_2WGZrQEk?NX2k?7?fe}2qlm7VV<2dbAbS~emZXo! zAz||OfO(I3v-wr=$KLj&8mt_-U^(AtCIIPLioFVeDT_5^tAVkBHsUzTv38%CX6XD+ z*K_At?Qs8RidX_j2?HlT5C58>?DKD)Qt=(0bY7fYs)MdDP9mq(A4jSIq~RBHHnrZ` zEFLmL9Sa|1C@RqxiZZS0OEsO=-H?ErrC1J|grQ~XQa?`7q;6!I%fE1ZIsGT}Zc_wy zBr+gx!J;8AOpt#|h6rzFKAyioF|W_yq6v6#>-AR%@!%aoEV8OeI3{%Rl74vHF^&lC zjDS-|ZJl*|f_7PbIclpJ4vXtUc{0~tn^@alMg=!+adEYHAoGQ*BJP(K(OzuJLJL+A zf^jg+GcmWF&QtM%uY(CslTr=7Eb$W88M7ZMqaQ;%SWg9n+S^tHp1jcUHj$&hO-Qy6 zI{2dj7=^!bC;NY*!9~*OJ#q{5|2KmikDlVal*E*;F)_dS7{L(5z%8U2S9s>*GA4Xn zFeOY-`79dIBSKI}C6nY-8KFdOU)Vvb*3hHZf7Lr1Tee#}*qiE?BJL`EY2RW0`ANWB zt?P1L%=zy1YD#>$r8~A2GR47V9JZ6U8&}>Zva5?Irdr&euOFY>{l~0k3kL}US?PWt z*Ix)l=ydtMw$N6Tw@$-({e^C@S1#^}g&Pb90C!E@S*=hrr zb8i)_X;{`KPBOm#gFOMnC?9fRq{JJ&3%Et_^G15#Y(m^T8#LjQD?tD`_fXN+&*D) zmRN$+u^=3>Qje9Hv>qb~O~c&PrRs3pVVjmA=5>NMoD6*9f4{umv{h44@zqH12_B{I zsv>WxF1E0RR^BScQ96X-P`#V6nj&FAfx*NL>sv`bp2B@4wFvXyfD3uL-CaIQY)`_X zsBZr-|JtQ>hZq9KaLey}STb(8{m)+B=YCJ}2%+7v9^d$&<1A9>Kh7t3r%aKdJwr4X z$lbO_Zz4rr-9H~-e<5G(&`a|{1}ZE=#uicNw z8W@?;b}>S+{|R=W@I6dhQuGx0%)cxovc z=TV7jWuqlc?~SP@F_sc2XBP(bnKoMGw%i^Xlc82T7ZCl$jB}=yR?V<&$cT&z-_NQJ z!%O!I@p9o(9>785;hJM;9k<~QBqSAM`>uSfusp`{ShlJcdeVuB9#IqRvysu8 zt&QbP5^&d;kRvMA%yNu_zfTu@N)JeiJv)4;rOF~V}ynlsy zo@7Ao`r4Z{$B6tuuf`I+YE1(!|Avqxh6MQG7a@LUp1MAXLWu4}v#VSXY3uOEU9t7y z3Ll+UoJRE}Vnl?MC6iGq)(^2RAA2PmA-YToP!*P3>t*3TD-cej$A?`hp^tGa;%CKB zovz+pM~nNcS??B9_27%Ow&6sKGQjMg?|&qG8A@OE$@)HDYCiWFKo)J%jj+SA5Bo_h zoN7C-jrrhtUf-FrI72c?TKscy(L(USHtNHu#nUvEpk1C%*=!tLC>&mQ-g_6}CSdG0 zRIHcwXG!|C%?2sAjJ4#t<`J4bUFx5FtoAykOw`MCOLn@2A8)PiuY3vez1>O2hWY@4?n9tYK>6?%jrlIuzMP#r#&oS%c;6YZ_W9mINmGBUm_m7P3vP zoQ5^2(E6?W{b?ON@wqXe?AtR^5ltCs#<>b0wyg(*1VxCQp!4 zORLx`Gc)l?@5n?)O3#jaPCNq(KaY43Bq@GgrI??-Js2zt2f0nJ+vAQVnfH?ks3kjk zTz;hP2ycB-&h@@mef+85<04^SxF$+j=|*W8Ge+bA>GYk21E-7XE#DnGsryX)ViLe( zHF`;;v^l@hDUDzClMKQwu`~ZlPEzL1^f8H5sfO3q7V~iQ6<3$A&iz!pzqJ6i7j=)x z_m8at@Gw+;(&4K_vCVu+VizW7g{$*$u@}*I@4B(n>okfjPV{R47eoAJBlvo!YvB3b zHDqI?(9&w$1F5c|sj2M?TjyX8*(egOW@TKjz;*=GFWeMZ2$FDP295jd;_!)qE!{FC ze484Df4Bb<-h`bC0$0z^qiKo_x6Ukz*Wq!3Ruai)ijoe|IqfWj<@DprL$Qns9t=@+ z=rY=Aa@Qa4`iTU3A!)DR5Ut4~#^g&p?sV*rPY<^e<1#Uo=wM-JkI7o1KK)qd4&UR^ z5U)PX4{l;3z^Y|!c2V7H8^6IsC}JIYITLd!Z63bKa(fn>-3l*$%i+$Os6>1 zRaZAP*S}O`Kr7XZ6ElNn<1mrAN~KsGKhT4&+Y$v|jiH1~n-3*s-yq|PPxcbR!aiXn zG`4rOkhlHo;Z#d?C;8O?)Z|!pf+YvVJEIF3vl`qf`Gw}f$AzikYY)(krsReFhKB-sl#Oh9v7Z8i32guEzKh5yXtKQ)9e zj;C(rdI*Rk)3G@R*^{rReQDXmG>M_0WajE%ScTHm_eszY7}IxSyl~p1m6ydqqv!vWofor-b;)=GVRb#P5&39pAAqtIxSQ&|r~rkbb<#UXStG z#~bX9bp%QU;cjSoP$pD*MIBKQEz*-Y{QPOV#zYDp-=*)+w__>y$sJ$8H=C4S z@%JU;Y{CydiB`CJc6V=`4bq&(Jw|5Y@b2378PnzEw`?#xJ-Kr}>pxxm&;J52X1RY@ zVDDCssuM}3M^RYKAX6zCHkZF%)=UHXLq74e2PB?>e6O zTdR++wyd&>gf(q-;z4vb%V26HPlvSP&BP&|$SNSrSg&GP$i@pxQ2pySd&qu;=40_s zcT@GI7M07Hh-QyD;SFXMt8Q`H=`*COZv4yxvSvBEch-&(Z;?@BqA5Bpgf;JLPtTTY ztAFa)}Q%SCEVpzb$}6c_ljoASeG zL9T3%lV%`kE>F2QK&H%`fWyzL3ytS|BEKuXR?-g_4N2TdBp}%JQrY*H!Y4Ehyv)+x zX~&_n-w9XA^jh91JYTF(m0sIER}ysCxM7|rmx+Bjosx&MZz~tDp*r!E)t_(-+aHe* z5-|{=Ld5YRi;EbwK}W5^99(}hHHpWL^h+8pOnFhzxz~^W_ML*5^|327OMn<74$ooe z|4{YTVNtYi*svlJQcE{1u#|LncO%^m(nv{zG)s3kNQZQT(v5U?mq-f;yu%7jmuW{Hr?PEUtiS6G`o@+7N-d!05bg04(Qb5dXx@9)Y^GwUw`=s*4PhFI==9K~j1m|8qN zdpcTisv(qIjON=n*{X+uJ=djAWaBD*{>rwaBCYdZb-I#O|4}w|VLgd?F!X~2%-TQF zi7S!8$Z1i|Kbn#nP}zZg1oHr{wsDr~X$o+R9r%|n`p#cn_a6cxj1CBR9%wLMXrn52 zdjm7M9M;es$qJB*ZvJ7ZuO)K1L4z54VfdF*a!d_rID?;rao}s2{;&yQ!Dvhg*L)5s zMa#<}x_noth@a9u=oQWW0XB?E4u&@L?@$7wp29s@3J}>YV%*Pf%GSz}Ns(wXE8XCA zcz~^@`ig0Hdptjp9g_qX(c4jR{vo8sc<$;QAoba|4&6+cd;Y$Sxog`^)*S`ZHakfK zj6e4Z(=&30k9WjYIf7IeTsT!96WH;2n#bwthuVK<;PW^Y5+|fvbRZml1TH$lJO6g8 z=#>Q=daa)Y>#=aO?o>+Iw$n`4+-b#3E4DI8F7x_sX^5efCV|0ay ztrOf2Al-zM zGbvJ^`q}0oq=VWXF-tRBa1zEM{wG#)ee4=uM4^zMLV}pz(*=JDT`*u&A(M%{ecaCC zkB!XWJtVa%*3+9Hv~T{ghhdgk4{X%y-^4z8MIm%|kwuRI4I)a~=`Tc!#CD>3#Mz2B zzWgm3#<9k;PrWaGea!O@QJN9~GwlqbBtR-_U@yQAuUZf<8S0P2;(C9iA?$4enOc|P z@8mdCIxGZ4T(0#*>@q4dv7{jX6)VT6 zfw)v85hRR5{6NJ;|Neqj+WlAW*J$U0#c{*<2}?)s44 z#285wi>gVXp`~@S`yb!)KHST>4t3a55XM$;|F%JF_H>aubxoxg4?mbLEJFUw_cD*I z+Q6|7yHVhl^x896O?vH#C*ZRV?h^k#na4cW-bjOjK5*3Z%Bc)RL_7RbRs=_jg~RD$ zw_E?t+`NnE{^KCXciBi(6d%A^@c&n2Dnm}+-dRq{zT6DHc@YwGQ3%{>#>H7K`*-x& z-8q_Vm#z$tj~r}m`SVEuUWxqob^m&&??2wj_n4P1h444frGT85;f|fxI4J$#ov%A8 zDn@Uhglsk+$48~T#$k`_ymp6^)~Z)?w3(cB$l-CB3~VRzUi2oTBA2B&fz&;}EA(k5 zaYe7N^7V%Ova9GGV!10tfvYjA{F-5tU*ee_vq?X8js zP6R4){<#5Y@L)X1q$ES3sKsdQRE~|!JMnTAnh>3Yw$eSqx1$O5XgqputFEhK(?a%t z-6Nc$PqEyi`Z4(FC-=0dgJ8kTgQJyix0<^>Nl5a!TfnjK(abfC`tHj{5Aes1AmsU< z$-1h&aqxDTzMG3OLW?Th8VF209>)>aY4Di!$|Run$DX(YD0TL?lnwge8?R-%rbX8e zL@xH7cNWKp$CU}JSBRmcb4ouzw!gnAK#cktj0RjIamTV;jWdh>oz64J1>->>1s8Jn z3y`Sm*8n^1uJGEO9_T1?Th6y@!kZvF0WVr`@@R{89Zskw%`ySV&#wi?&Tg(kd+)_r zrBd(T%QONb9qn062uj@iu31O_2=>!&d_Cb83yX$+;; z1o<-Rde1+JIv#sYPJ$X&#E_Q-!A1xZ9%Y^bjsVATbwD#liGzQ`uh_9Q+~zj?g$bhY z7@6{zcoO;|;sBK#1b{z5l6mnzS`z{~_k~lz4jw^{?#MQ`B45QJPhHM4FTw;*WUhip zyt_whMrZkQN5SfOK&%QSGeTS|8K{_}^?rqKpLW1wm?zIKa%i2DeMIYYnrr+Vgn7(w z7R4z|82S3#WMkk@AFIH zQCtQTr_D>>5kiE9rY-c3(i3Y4*lw2*ZmpF(PNU`89IJ}f9{);0|NI6T9vm1~JJo-& z6FdV5P$8b4_0Io+YcPP@HJ}okjQ>98Ndh=NGhLAN4^9fB)4&3rU$C(L@_!y60tfIp zFPr2v^I!ZGCIbPxC8fjI^M{(1trov17UGGH{?Bc~!hP|&ptYh9JX|}U zCN_m5y#Q+;hYv5ozh~>O2UgCGkj0#p86A7sy6`9Sa&PPy9f^I?)${+h27|aT9@9iD zrUsseUqj?k%yhvq6lqk_QEHZVn`nNPYzjv95(m@+?J%Wv{mq{?MbC^M(rihlixk*6 zI5;eRt+Yr%l&EO94~y}b@TtRArP52yL^EdOV(ssHt{?BO>kg|Cex3%keoy+cTCar9 zROPnr(`1+M6n7eMrIs3$#k>~5*jgfGAY1jZ-AI)5SMxTiX8XfqQHK4IV&{sFvd}** zi8Aa<0}TX2J?maPr&tjJL%>Q(*n2p3{P5inOgyC+_X}!^g-{4f<`LW=fzapocfRYX zVa(ZmJBn**MjnJE-m?H=)s78;)+DqZ9v0^`rttyx5>qck$1h{X?M1*k9$J+a9F-#R zD1@h-weGZX>9F}^ArE9zBtgKrK*!*+aJ6jV;+MCHh_#sEKb|c<8GY^A1(`( zbZX6H+}_cL!B(b<_d_rw-%y~l(poF>9zvGkpyB7qk9QM(_J1}5dLXBzu* z1Oj{>QV-*N7p5@eO3>zGFs{LY@7;MW3KL5!=bFbpu~)~#*$dTwuv{l&BN6-y3n!C6 zcMjO3$BfS}8jP>x@vaD*-r!rP9srr2?fw88G3wXv&hkP@YL`kzTo-*r|KBo8IA5(T zm>Tb4zzy)Gl$(MudN&ckC7!@jOKW=$r;ve?^$$veXq+uJJ1YFwwhWMu3h|R1JM9-nNtuAiTj3s>N2NR zj_Lp0mZ0R&+afNSFKqsv5A{yFvS;(ZCMq6y7~%pWaG4tSGh2Y7Iy!8?Q2DThCA49? zjlIwEqRhCXk)AJJ|44Fsdw_-pyXLx<;n2C5Z2;i0D_(c$Km?*WnPTQA@COol6RX0i ze>x37&=`K9;I9z|;;`v7w3r5+$9V?yoQ5cGBqQc_>MOk8K=&m6pT`my`$=na<;8VA z-d)uHe(CSp1|aE5Jc>ACsc`c+J+wa<&TWii-X*jmFw^(>VO_VFSV`ScXsg*7@S|O9 zTvFM{^sCtvD_JevgKnQ62#3abS0E#Mi@N`Pgy=SD@ zx7+|)@~vPwV)>_vIr$5(4aSYvsc!*T#~y*hP-~C`lFAF4^oL5qF7iM9*$#OOAFOOz zvxdS0?#gejuDd9Yo5hc(JTHicV)K6yPo7^gYY5XWqLoFrwyUPFu=kf{FaDrmiXFJ$ zFZD}lpiiiK($;7oRdzW4^^9t=v^JX={ncwWx@}`YEg`c^(CK{V4Tmmej<(<5*=pZe z(+R4}VBw1Bzo)Msm0qyGpIj`rmyJXl=lIH-VJ(Kc_CI(dXCX(Q4 zi&+!6TCT__1U`OHv+%19<56BoU?mQ+5Uym$Sl+8fL2(j}fZZm<;f!^tJidSbG+S&d zyQuKxy|72&R|r?b90RXDO~3z#cn%`G&b)9LW1%>>aEjA)Rs*Q%#@xTT4In-f1jJ_& zE#w}`{w1EVxG%U1Q}=I31Bla5zQkYHb(b{%!?SiUV9-#G24!7_WRDcJzK|9w+3_OO zj(_$&0yno8tS2SU7Q<)Y@?HtY^bQbF?^*GRV&NNzAfVgoMGv?fQ8=M6{agC5{Ea{d z4G%;eY3zeKA{D9uRu7nO;NLjg+mMN{pxf#FonL7zYWT;UQT~gtlSKqYYqecuC8S7{ zreoYU%FC5JrXV8FW*n46S3i&UQ5(p81OyJ?B=205DTWCB|mf9wGT~kkpF6 znA6FI;?LU71T(C|Gh<S8kYz4f`u^f{m#7BBNmZ+59ctl?mTWz7B#jzAAYrs zy+j!naCPXq{|jGW`JbTEjCF?Za6m5iTr9eGINoZ)FvO@S#Ntq`+Xh|B0ur{^{ri9r zzOnNc34B&5M{pH`yl^lgQLFIFmNUJ_{bBK`P6?eDPgtriE=--4&y49m!2K^cbRY(q z`fDMuf$dDnAdXBANB|)sEw{lZ?HN6ko`^eJX>pDziE&b-yUN+|G{B@*yvnJ46x_-s z`A>C0fPDd1OAToJ>$>24P{3V+tB`y}Ds3PZJHOCjrD4~a%jeHG?x%}&)V~PGdWV(@ zp9wmp7V9{rGaDHHSNs6lTV z5qc$fT0cD+j!lOQ31S-tq8TQbcP8UO{797b3Z%@BwdUP%WHw=-aUeHi+o4M8`^UJg zfkMOZoT$$%+!R?+FepLH*`+WhBz*60rGw81=+hTx&;byiiTlI{uVt*!cQV2k@bTou z1(Zv_`SLduinrB&oRUHq2qp`%*FvNK$v6t*qB5X*jUt$!LiKb`#X>{ z)$r*eWe@OUSh;&QzV#qsQlG6qKi$TFokzQ4sdVWHUiHgm%E*{q9JLZYbEz;6>$snRhw`RBu(i z@#dLOc!EZq~Y=Zuj^?qgU}jnBDv zjeh{js$ji(*a97q7{x9>fh9dW)%c|5cpvXsrP4nF@FoZxa!KgW{-GJwas(0G$e|@{ zbjtXUW5?Zn@V;~bWO>G1)Y35B`Nvkin3j%#C21EJ7nLr`h z;-pB*=B)A+Us>1s{csSZ!|yF(^;|-@@C`!AUaCMyON9u>fL?yN>pszI>2qPzQ0<1A z*}wM29UhlGy@dz7^|p3(n{Rmxc#7vTxxY>!tHrzgK4=3N4v{DzQr@eEVZl_MW)*eT zOt;_4lzVL4d0r-i^rcxBN6o>9#b>2z)%d1A^C4JHdy{9fOJ}e_pt$CU}kBK zHr=Gy<8~Y8(u=HbSJYNy@Lg%le9iXzG3^@nXT^ydvjpW950r?B{|^Xk9izp zcUYKy$cWu~3Q@cq_ay6+xV{m<4fOL9i|}|xW7tcr!QKmJvmUb=P)ieel&wrDk2hlk z>aaFM4p8)Deg^i3J?+IDL%NfZMX6&HGD@&K&cZyL|tA)-CY~`$+E(i>HWdi?0<7IHH0pE7raj#T^z?gYPc;*zQw#1~f9r z-pDE>rRyTG5HA&x$GX9`E1t9_QJnp@2?PrpQ4!hQ%(M=s9#K_XLdx8Sm~n zkqy;Fmjh^#xcCLe+8$*ZS)!N;_^%b`-9{&XmwIJ~x4OleM5HR+h86u;N~gLUz(q^v zs5N96W&JGOa@T$6V=L^RhoSeSgb(A3lmxmoK9E>V%zG~_LOBrdy(%i;3q{{5Jn!h# z?AV?X6ES-#O97++E-7Oq2r?m%w}f1+8Xq~eg56pTaN2DD-cvdrs(z`7y;rZL!gJoy zcGlM}^50*G)Y;qAOHZPWFi(;&0d=ayNFxgWwTwm?)!~42*X_&FQj-`z6z$+O;?r(D z35UbJ3B@w>t{(b(mlSC6*jUwE+{b$I<8fqkU+L78F;m}E67@aXtZaNAYlDA`*h`}I zO{MY>i!ygZtrh~e%1TSuD!sv^EeWQ|Uo$DkxO~5rHxJ2aa!QA$jDjuwkpvZx!vU$T z1SA<%jo>@CjlmC1PC*{Y#bB@Z<@owph`ha7ceM2(xS}%eF^libqoUvV7T=(OsA~3`YZp2=tTE;1Lu30i-#kXIK>ah80r5&oOmq8GP zfoZpcARvmo7$ly@M@*bgJ3QulS$t~o;}~Tx%{0%ujR^%?y(?IeK;Co%954==h!F`e} zIrj0c;+eMD`fHX8gQQesj*f<81YPnZ+V=fnx0ZQBlgrh`yLkcgZ>c}W?G&Q#7HP;# zA;kes)4LF$xBQVCqu5h1TT|35!mmh*JH6rTz?JR;-y;!@Iwm+v`8g)<;ny1cEgjqA z1CdPnFK@E-XR71+_VJ8GiFj2>S{WQC!FoH1SHRj_yMb_DOQS0|2A(K*@9e_W9b`$$ za1$x2xCDY{v5Vxo*xB85RKGpDr*>Zc{Bc}I?Gm>$BL>knz$r4YHpKE@^9N4nEOozL za%j(oy)UM*DJ_%|?+?-$Cw@C)0T>O+pZD&WgsScN^1V;^Pc zj4RD*=))#$T8O%cJ9Np6&L=a`xqjyoP|9&s>upzeXQW#1Y$WM}TL0Yf2B>3eEE6(P zEf)zImmkn~w0e!d#`H2&UR4%pswbv~U&I%32X%ZuCS}h^BGicpIOvIPxT$snB3}z_ z`|Hsp!PbjQIF~>6ENvjl>wkb+yHij$`psW}3qD)0a(FaVd9g=^x=eM#U|MTgJ)f84 zw22%f3F|#EZ+wz}LpzVaK0=@&7kP@-V$YRvPbOOHpe9W}`)=|bodn&E>(1&F$oQe86Y91;kFv(r{Wo9yM*q?9S*11x+obng z50lU(HdLW8;h`TF-ip#=vR2@*RwOB$QydSE);lKcB^H#Ao^R8QzJivgOFwP4cZT4K zH!I2y%4u?BGznRAk~%n~d~!eKBmkj*?*7#zw%#P|y<8&(47M9<%2tJ~6#?!H!mIEd z9h0K?8uAL!DU&(m2N~O$(t`J+{6iLwA*nKmtl7|S&yL}+^^Lxq)kdUc+r|&8Y z)V^IfMK1Y1)vP=Vyv!u%*WS0q%mijF(r+=!%2`5Rx!0#jJ5BUIXbBS*AI_LxMZ+9y9k3v>8V zaZz<7v^X``(_BJ%Jj{ac)>E$7oTDp$b-ElLSvarPUGJ@4GgzTRmaq@MGH3aoM+&lJ z*|ZW4orki;RI;`Yw)_djMIF{i(I9N%P>;ehfsh<=FcaiW@D0!`B1uV)^kwM2o4 z(z#vI_vuflEWv1pIbES}exz4zu5P4MhcEy zr~dHyltPkMUY~mKSYWb>CB}l>hB;V{XvK6u-OQ=Qpa4KY8K8E{!zkC}7*JFF9LurT zk5py2-Pf`)EFSa~Q;|mBzv>xda728IQm$CvH_{vA8EhHR(}%=?S)-FW^LDZyV?S8d z>u?z1aN3RO*ddxk-D>?3PGJ-^!M$a-^04$pi6i|4Yq7-JprP$bbL0gJ9% zSZN9a!;7p3q6cc@k1hPtrDlfeIAya%-tBh3K`*CTcfb4Hm9*KI#v~)hfE@Cgu1L+y;o`r*vPqJP69<{<0jlK|4F41mP-e`m`Kf#%RQa4wKh2Sh~KAZu|tZ?)_GLEo!T3 zqje<4M^f@b;X%xh6y0jAFjnkJr<+7swh{+F=PnRL2AXA+Xfuh&!14~-j}dlG%QDva z-lp~mK3HoPlX&@kwA#rBbtn0tc%TZfiUC!CZ6$oGO#gw;?9%J=5|S`cq8_x%WADqV z&Zl!)pV9AvQvEDhZYEK)3wywJNM1)<{}rkznx-Nt&huh&5R&n9G6MdSc07w8&9Oa^ zvx+VQfiVo_yNmKzLw>Ump0xR#WFmH;nM{i?&p@y#PE}eIjE{s+%dR_$=AY!d5muU_ z^!~Z_c=Y66Z|)QcLaB9&A0^<4eW)|qdb0S3R#2|mD(^%VzeJ<%w# zr24Pk=iM&7n-pJE!c#g>`U)!4Twn_`9jWf~y4u7gJTZ_(@XZv@IZwGZ7}(#$=(_fF zB)MN1(k~po)d~oC>V!q|(+c~Oq^@xD1FX^F5>vUhU?P*;Tq7WrWUruO0G#Gu;F-Kf zKa^P4c7X6IDo>G-u$+ycJT$I1g}2o9dt2F8@>sig$bTf3uPjB-SsS6tkXSBDvBf&i z@+^q}!|o!o4_FV)eMmBz<~JzeUgm#ROt?-pe~`#2>s>lPAQLyJ2}0nrjEHHUF?you zmDc%|`*y2uG}BT~q^b0p7N!FjH8!xuJi^jH7?qL1p-3!(#C7u0UN*h=K7~17z$zw^ zPd^M}6MOQV6d|mn!oFl8soO>LodgjtOT-UZKy~l8g?_PB59%DPXQ|Si>(ZHEc;|aE z-`3`wyCI=1!A`+)O$0AAH^cLpSF+tTJ56@Zl_Fr#!64dG_VJn#<02TrRXPlhp)8Y) zwk|P|EPtH;Bj~3SHI}*t*Xl&BuR$S;fJU%^+3 zu#G47Bc7Lj?+5eF1+FauJ@yM`oFzIbTPV-Gy}PvA^GlcOIFcI6%(~3;gZdk0<0mQJ zj!iDlh3e{zbv>KsXw%q~y7v^3E80M~-K%y3$Lwn12-{mY=O5?pvHkzchYx>vp0l@N z%cx!Xn`1pcha_W*&8n#Wlbhv^N=GPC9k;Z?Pe_swZ;2*tM(bS zqiA(FOrB^VST5xkh%|a>-r)wMX9M6ZosTreEBfK`K<&$417#a(UkD{E`+!_hhkxqF z1oR5mW*J#u%FoMUc zz+M_jMAH(ABXlt5=s%^#{w!df;* zIt<=rWw-a~d9zO#mS0XrAb*$Fv!>@)Jg>jhLnou+Xn3XM!pmS4TJp8KOy3TsK>3fy zt>1=GFuide~5;~6|#2OMb9qvF)x=? zFyG3lN5R_Iv>q+teqOn1@!flVxOl~;AfGn`3s2E5ZfH1dg75=Z3E`Mib&d`*xIV(o zu5~~t34HPZ!Ta)0kTek34iWf+g|%U0A^v#m`HP4+(cG%Z*BU3IxUs-doY(=O@a+-& zt>y$vx{$W$pU$w$PaUb+DYBoDwyJ7>_~lxbncYi6Od$euVIsSnf^_CF=oeI4f2`s& zd9X!Pd+B{*V0HX1199ZawhNio@M_mQ`_UMhShS$6337L4AInpRmmygipQ%ptZay4q z{LdYr`_k!@AbC90AdJ^@y!G5379i%%aU%>2JSm2KvQNI97%fpG_?^*Nu=#krKEHUS z6e0c~F2^TTr9hP;eS>219pjJp(t_;eooZVc=b~%oK;->XKc{mwXnSf_fpe0XhjW)po>LQgn4 z_(LU|slJvFbv>RbOTP0QUqRw6qhV6moe2K4ZSu$FUQ0X6g6CmiZf>8bVC4d=MeKXaw)^++iw5WJY5FRLHM zFsnIt%-{Ib8VAg;Cv&B>~^H1QtL^Wiw4_yl;V)WhHcUq&V zQ2Q+r6uUAAHwguc%=VDfR15>+V9@cAc{w5hbL^!=5WpI>L-k4?loR4OiM zw2`P=i3g9!bO}-x#hmnvHhin3BrUqsixE54gnbt!{FhHF&#UMZK3ur`)0Uv)g7gkI zhInAoR&KiGG~cQ78zn65{$;dwaZ-=N+?MCX`@*H7wrD$YOK0wqX3etf{KSXV%j??E zi(HiCjU)M+@wI`<+6m_DnQMj-IiuhDy<=m6hx}{Y*UjY0^av%|4O$kgL)am%POwZe*{qA1W;_B%;C)j1lZQ0z^?6=%i}>S*mjXBXLgIp@s-++7TTrWtvio8nYr-pVzf4dgtqFZ8d*t#*)!$2`Zi*^9Tki0 zqUa>#_2jlpR%+VAT(6&Hn45m&#p+WgBAm?u|15F2JDAHl^0mW}JtEh|E zl~n#PsOa}o2T+}LxT8KIf&rNq)sU+D(&Vg#bv$^Ddves!`zO6;Qw=-|tC#q;$I+Oi z%qiCTl}w9jbQycGv}Dw4jDe)aL$UV(Jju-s=ddP_uNqZvqf+q7OVcZzS4k@k(Zk-u zl<`Qq?oAzV$pmO)%I&$PNhUc)h(=;#&~aq57o;J^^aJm?6mU{kjGaPy zt#^YfR(@JTu}>4=BPA!jGbCP{)7zsJeSP?o$&{K1>FsME>}^AW12lHnbR8)N32;Z7 zl7j^8)^-T}Ma>;)Dp*houg0j(j3y{71j;$9G#Z5&qMphVk9Ajpug)1LQE{xo!)ac~ zeIiY-dh^g1Z;a7hsmSV9NH^Vw9)?@19xvWSgUZJQ-9}wDSqL2pghzvg%6}u~$LckV zmv53o9Pw-ZI+p$u(_CCe%Rl=iQdI!_a$i`pPU*%&K^nXT&&lLZr4KsS& zZ81E?D#09iaFb84P*FA=6_uL#_)$Uw|7NP_l5dA)?E}+~{3`6r<>O@yOtmiUWLD+b z=*|q2R@N95w6!GRjcI7wOy|yv&WNy31U^!qNlq;Pk{6F*HchTzy_~agfn$K3aj2O_ z2dlJr3$X>yV^>x(VvqZ?Wg16iEq-=(?$>;lZ1Tf`{*jj4pvTO@;jJWj)Qzy2OzPPW zyw1u?CVwsWU#M)XAV6gS1GSWHAsHCtm612OtJ929_nI`SO!)$kP3OerHZFo4xQ3gf zc+}gKsxzkth?2m;8dVPwxU_L@KX<%k?G)23mQjnY#98c}r@m>FAz&c_-zmWwcOD`V z-Sx;)YuHPdbLeP)?^7;k?x(LV>hM$kSZN=41LNvT0}=7@swTUR`v^;Cp`S&2bm*kV z4263h)+#V>;$0yTC|=J6HN(mE#W`FSC-;PktUz?nnY~NC7lE!t1R>kdeZi)QQ1JTyUT4KH^{&= z#u+0*ne+3Vzg>LJ5;OwqW>6=8|NM3Cz3QCXbCQH0YTGr`&#%x+0>ijLlyr2Hl-pl= zO#{>S1;8>ukpM%}C9ZVP(7`*6di7w4$n}H?Db2MT2Lr-~ch2GT?P@NU|QS z9}D-E+3{Q1i*0wrSVQeu+q$@Ry6y20@GXXtrZnq^kVL();<<;310wxEql}#VeA>nq|1lwGTrN=*|u9dH;>!ZGnuj@1~;C4XNQinhpbCo>F;N- zG||3Y0XUCRQ6k*_g;U{_>?Y$oqRYYP*@=H9)6B&Y{F3R}y&wyKh^^dBt|$Y2uZ9k# zLPgR{Ealt^_Rxh27*zAc%p7zNXP{|VskQl4B>$~I2rOv=lQHU74QZ69^2pDrJ>Yyi zwiWpHo-F-0!fffD%4_0Arg*fCR3srLRb7I6teej(XiC!D@bC0giMA#CQDk!CUiHYuEK6~-M&dFR(#ozv2{wrI(g-`-oaV8b?9$+S^DX<_cM#Wy3j9zln`*#uVGk zMpMkT+RlC_bb>fks4>O4PV3!48>><4`YBEGwC`F8`Qa9QKW`<$`&)47jv58AG>aT3R6I7efPF0l&7&~ACp}jm#cJ+e z8hOW!RU?vaEu_5*s8RL%uil?Nwaj2AuYMCKY}_KZ1k=x=(?Nne40`bE~bAj%uB=k36qk< z@#Zd?+W2`{?7F+eNPn&8@)G|uNs1MG(* za(p|C^P=EDWFqZ%rdX)`&15d$(m|$pazt>_ztp(5P6TiSm~70R&xve2O36cBWW&4V@I6=wz~-d=~!4z47WTMWwFn$u%x!R>{kO}83UNV}cFoT7z zE=qra)xij(id+caT8LM-Xpc{);xzrH1iLM?E>xYR1m--~@EkS@^P7`%aMfv8Y#6))!;HG1QB4|0ucf`9R^^Qx z^gRSIW)}Rb06qv3+1Jhl64CP0s$;j=#ppc$sNVuH;^@N-?~ zxhuw?3yei~m}5jWu`U(H_e9d(@2O>Su8~?F110ccm&4` z4;(7sj?r9+y(0RoW|{RLJgkDN#$VH&1Tu}dVxu6msf!uRxo>l9cJLY$xf$D~H$**@ z&3--)LTMrxYpis8ic|Bu_}a~A+}pyeAK|LNi2?TCgRKZc*Ht0PxEwC|_=ZxjCbTcK zX*y8CgWA17r*c~XpFl-5Wbbgsx#m7FUTr>tMw5PU^oU~ROjUA)DC$4FP;$z^cO;)lxY%P`)UBfD2Wxi`d;CzzH$;-T6R!w-T zdCaP^*_SmLgT2Lmb)8*i5u8jB^9Bh=wUI1hN;S-eXtg?ufDDJ8-mos@y1PRJwV?5T?&4y$m2!X zO#1rK0%ba_E9r|b7M7%Bwk1mGG06V)K0#f`f|PqENtnvXpG2*iWylOf;{X8|W>aAo zhTLiU-J^e440~L7{iGaYfby(DQY(TBN?n-{TbW z3O|j^YL4nJZ#4^Y&-nI$GrgAGNLO3M+*w)KM3i;ybn0unD>d_5#YqNY*ghKv#e~Q6 z3r#+{SxI!tIu1VWPGSDaxmN>2yyWdpaYh2|KkoZ^w*z$FVc%QN8Ie^lYS7L(eYFxA zy=gdh)oQ}@Bc38-(s-MC%1as}2IC8k4P!Rp$C47#o5i{hGjdL}7nNo8w)C8gU3NW< zen;GCIBS#8iyHZ&!$Tt0Yc1n0(i|q}pv^3`p z-b!KUMXYg|?YhPE`d`VH=ECNs&NooL&6)JF5Ihvz_eWD)&vJl%fhtijMrt%FtTb$Ob%?SVOQpZUj z^g-V{@4I<-zB8)PL;0v%4_8b`H$Z4giokj?EPD09ek3B~(6D02WDEy*^M*9|09FNC z^7o}hG{b@Bc+WY7tw|C00dm4HzJ)QSqMJitFCk-60}pq9uMUMcX-}4O_XAw@G*EdZRb=8S1Ohl$&Ae7|+6=wCRff>ZZKJldXGeh&zwGl1@jnu77wo>8i!+5(iGHM-CWfoY~@KxTu5?050 zk?vpjf2(Dyg+zlUb%rEU(}JF^dxWz6H`p0Wxyna3GLxcGzer~+ zPSEBxfP3I7S#B%fh#HX*_oW(ffj06LdS^OC4&|En!}T@xx#Ru^Tf7Fi6sWX_R=XvL zafdyN@2fD`PoYKr+~?T*&SlDRbZx8H>Fa#6ozl5ZmaYR>1c}q7?BpYOhq`{A;r+IHLSL$U8#VNs zSMI0ryR)yj^$Do=&US5c*+`;qoX`yRU1>y6E{;LnHdeBw<9THWHZw zX!}?O-86a@q-3mlf+&ovF-q6ktr31cZyz}c2!umkafM6S@v=i0{JcEUbn32aKTemx z!52HciU?Ydc?0)DW>g^Z7~Anq$tQn9LM|E?#dbcHx1 zfQZacO@KQb;}dX)Cr_7&boAs$5|Xyo6?Q+iu&O1O$n!oi59e+BB9b#7gbI$ zb38SWU75N($D;Op2{TP+N&AFML^w1+<_YSpW8G_r=YWqE!R)1_xj+()3-Se*-kLiO zpXcW#?;>kQC{RZ%A))er{D#;}8JR2Zh2^g+N?^E`ym5#bWqh5a&=zC?8;eksM)^dHsuGeKUi#YLR`dy!25>udQZcsVehx)MzmzC z_A3?6ZdSiv#tCD-<_bpt_?4TpX1@RqCLttf#y>&RB?(EMqjmNu;UjK6f|6E8gXp8N zdJF8@yi$WGJz7@Tq>x`6#6Zd*^wldgCK+)NHMoUp>Gzf{u>_IC%NC_JRm1T+%hwu| z_tB+VW|KU06BtqIa1wB))th9pWNFtVMlP40kL}(RPdW6pZU@NefM|B;j*Jj_q$_1Ma&;5Z)d(` zl93UXFbx6E5@aKZIe?>&CCp7P)3iU7RTTa=*{PX|jlBMNGp$k-mc!^^qf`(&BFu_C z@CqC{-;H|AHD$K*CX=yUJ|fxIa*iSLUs01F=e7`#IE=wN3U^haK=*k{W{VvG3HN~p z)DX0*7D1^@nC`X1$8HfH-^+%RxdcVQ21tU5gV0B5qea>-A(fER^XH z1Tz1R)&q?rcRN93bf)NGulgTTR2% zuG(V|p_?fhI{Ueb-(+$kI-%bKS^n5Qu31uz5o#+9_azpCPq+Hm$tBm%FwMdOr~>HH z)W)!0;if--eaGsLc;#Vcbx0v{FZspiUFdY1iQQR7hx$9!5|A|Pp_u#A-zGU;;q}um z4}<5q4(rhU_xENHkT5bNzTU9Dh8Mm_`7E(nHaR!E9mbmk7UqyD(nyi?9K7Pe8I3;e z8wxo)lnDz|`0@KIe3^vp2<1YDuxq@E`bbx~G99P-L9P#BTlP$z(QId1nS4rxXt#q_ zgxs9|RO#LchCS^4M3x@V2)S6JP8U(RW;G}HAr=-e2Uz?G@4^!Q=9WI#l@cM)ME+&x zmGtJ#(0n}j`(CL;`K9M0N%)NH7LV@qfbrNhDq>0+-CAkM(~smkSgn)hLxSRR=KRo1 z4ZBuFZ!Bv6Ze<&ehdoga34X2fO_IbArkKyZe&Q!IqFP130td6uxSN)aa1-mcn@ zJf7zd_W0hxg)6;jvX4Q>J>yD}468!Efot3grj4f6xBe9w$>`iWjlm;zo;sE;-1uij z9{;`nQ|T$=D)iq8%mIAQd~AomE-mRbaT_(dNU8hWDfdeVvHAH4iWrJd z*Bd&gBqzo*!yDhyplu<+_+m;5f91a4nv;E2ylG|Bg|E3yX)n+di}3Au*E{0YnwU$# zNKytoj94(fSdw~#zCI|!#7EuSQr8XzsB#{(5Cm?zS@wl=gxsnAzqA+!OvjO*i`H#; z-;l-$-=qI7XAXcc3|zJ4crR1*o<1se^A*WL>39#=^(7%TIXq>8j$ZyfuHcOEVJXN4 z!)qZViop*=9UXqGzfSJrDXyLn-?qXPu4425ML=T7E$GXav~C_po$I}#AiUvmM*O3X z=V(q7o#y4|$q9@!s9?FX7GGcau0;0vIJHv1qXuULC`jN~#O|z;0C1oBtE9kXM#(alZ;PA|Ek?qjD6nAF+?| zF}vcQJ9S60BsvvHZCbM}_pN^1#J0z!bp&|8!Bcd47S9U#4Xq@Rt6 z>a(|><7^cmYNafhnqlT>qZ#qt3;l_DyL^~4suFY7s>u;ci&jEi zyGT1q3ZI@DDo20DYnVxDsa8g4;#_2}ViI1aI*M6MV9hXn0Q1ferrcom43bgc-)H-Vw z8sG1PumnyR3vOTM_8SeB?@V~~(_IYyuvom1*iDfzbF7h*d@NbiQvh@YR|tR%DZiF(bUzdTMZm(i>+N++tl=8LwM&l5WE~yqh>$5l{nPSCR zS##78c6fFVU?{k13-2eJ<&-|&@#CZ);i@8tPc zkDN){sKGL~?@G=H50uY64p3Qp+)^sr0(!8DAMNg(R9Cl*lh(Fi8X-JDN0dGAsHv0j zu&|&>Y5w+@w_Mdv*S1DA13_d{*@D7C6Aum$I9j?h5rbAL21=sEHU$r~hJ!7+UfBcICI| z4mTs0b@a}0keXo%aI1gxX4fC5p^{&lr5dqC)$(hn0=vKX6Y z@~}ygaPPX4Q)wA^FXdYies;=_|Fa$GKqM9e0p?#!T=Z&62#{$$ECcM9ESol_f)+u) zw2gWT|IO6xsdoX31IDt$gMI4nR>N!D*^NdVfwT-#%B%!R3K=DLb3M9V5GU#>=^X|CHU^;0m{^!hZD^ zX=VIU8j%~orKGtNfW6JaRD0?^u$`6wjZhnq!E4WbZV?abSGo=46q2M@iHz|V6Q@*F z%{IhF#t9o=2GvwTHASVS3ML!kV!sLliq^72Od9-}PS+7}>>ObZ!)@Pqvx zhp=A8)Q#mB)X{A!TIDe^B__zI4Mm9F!Az8%5Ld98V_z zo0D@2ZC-0E#(s(e*rmgb2n6 zp`++guSnCmNIy8`{}oy7%O+NX-cnJfM!xJ--ux!?hpb}}Xu}qZiYWzyZszJVgYOk~ zo1g`{aJa;NsrfP&yG5rPa5JoPjTTgFsYv?My#pUAHLpJ1Y_}^TF8CYk>^agwcUj&e z+?Klc1$zE~A2-Y=Zo%>0v#)86Z+LlpL#VbOLF_+H?k}VJZW$8C275x_CEX}>a*{t% z@>S(d)F|_9gpVG1{EXK{cqY^Q@%pvs*{n+uQilCHHEUQ&xrewkRD&C`qc!6M-d3Jd zn4>->yM31DVcul)K$<-k90v+ikjQ2@_0wr>sYuO$t}sl;EQMkzcr(i%Gxdb!)*JOB5z3Iiz_4T2J7J82bbcyB1!G26cz zK>mnUq4ibHB?t<-CU`O(pBDGn9DO<0ohzvNY`~^Q{ys%7mW)sc4udZBITZlCylX)5 zjylsXsL(3tERB4s#>yTmunomP3b4)tEWiKQ9G#r&t%A~Aj}DN;XAsG{Zrni@6U^^PIDW&~S1hnD)n>y3Lx50ZJQQ z7>bkwU{B<%v2MiXVuu1OAT&3e9?87_dKfD$b2PFEls>o+81Fo1(D2D~mgZ6cPz0zQ zgIlbBA06D{RrmqwM@&NLX*<|zTLF X^6B$4Cob#?fsd)7#pQB+*YN)WCOU8~ diff --git a/docs/drafts/writeup.md b/docs/drafts/writeup.md deleted file mode 100644 index 8ae2ff9..0000000 --- a/docs/drafts/writeup.md +++ /dev/null @@ -1,285 +0,0 @@ -# Kuberentes Postmortem - -## Abstract - -Kubernetes can run on a wide range of Cloud providers and bare-metal environments, and with many base operating systems. - -Major releases roll out approximately every 15 weeks with requirements and dependencies still somewhat in flux. Additionally, although there are unit and integration tests that must be passed before a release, in a distributed system it is not uncommon that minor changes may pass all tests, but cause unforeseen changes at the system level. - -There's a growing plurality of outdated and conflicting information on how to create a Kubernetes cluster from scratch. Furthermore, and perhaps most painfully, while a release can be assumed to be reasonably stable in the environment it was tested in ("works for me"), not many guarantees can yet be made about how things will work for a custom deployment. - - -What follows chronologically describes one beaten path to a custom cluster and some of the dos and don'ts accumulated from the false starts. - -## Picking a host operating system - -_"If you wish to make an apple pie from scratch, you must first invent the universe."_ -- [Carl Sagan](https://www.youtube.com/watch?v=7s664NsLeFM) - -Starting out on AWS one might be tempted to quick start from the web console and opt for Amazon Linux AMI. But that is not a portable choice. So at least for the sake of portability (perhaps in the future you'll want to run on another cloud provider, bare metal, or your laptop) it is better to opt for something like CentOS, Debian, or CoreOS. - -This is not an easy choice and there is no universal answer. Each option brings along its own dependencies, problems, and bugs. But since choose we must we will go down the CentOS direction of this decision tree and see how far it takes us. - -### CentOS 7 - -[Official CentOS images](https://wiki.centos.org/Cloud/AWS) are provided for us on the AWS Marketplace. - -To avoid ending up with deprecated AMI's and outdated images it is recommended to grab the AMI id programmatically (`aws --region us-west-2 ec2 describe-images --owners aws-marketplace --filters Name=product-code,Values=aw0evgkw8e5c1q413zgy5pjce`) and in case of a response with multiple ids pick the one with the most recent creation date as well as running `yum update` as the first step in your build process. - -### Default Docker is strongly discouraged for production use - -Docker is not actually a hard requirement for Kubernetes, but this isn't about recommending alternative container runtimes. This is about the defaults being a hidden minefield. - -#### The warning - -What happens with the common `yum install docker`? - -> $ docker info - - -``` -Containers: 0 -Server Version: 1.10.3 -Storage Driver: devicemapper - Pool Name: docker-202:1-9467182-pool - Pool Blocksize: 65.54 kB - Base Device Size: 10.74 GB - Backing Filesystem: xfs - Data file: /dev/loop0 - Metadata file: /dev/loop1 - .. - Data loop file: /var/lib/docker/devicemapper/devicemapper/data - WARNING: Usage of loopback devices is strongly discouraged for production use. - Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata -``` - -As you can see from the warning, the default Docker storage config that ships with CentOS 7 is not recommended for production use. Using devicemapper with loopback can lead to unpredictable behaviour. - -In fact, to give a bit of a look into this dead end if we follow the path all the way to a Kubernetes cluster you will see nodes coming up like this: - -``` -systemctl --failed - UNIT LOAD ACTIVE SUB DESCRIPTION -● docker-storage-setup.service loaded failed failed Docker Storage Setup -● kdump.service loaded failed failed Crash recovery kernel arming -● network.service loaded failed failed LSB: Bring up/down networking -``` - -#### What docker-storage-setup is trying (and failing) to do - -`docker-storage-setup` looks for free space in the volume group of the root volume and attempts to setup a thin pool. If there is no free space it fails to set up a LVM thin pool and will fall back to using loopback devices. Which we are warned by docker itself is a `strongly discouraged` outcome. - -#### Why this is a problem - -This is insidious for several reasons. Depending on how many volumes your instance happens to spin up with (and how they're configured) you might never see this warning or experience any problem at all. For example if you have one hard-drive on bare-metal and no unallocated space this will always happen. - -If the disk provisioning changes you might end up in this edge case but the cluster will _still_ initially appear to be working. Only after some activity will xfs corruption in the docker image tree (`/var/lib/docker`) start to sporadically manifest itself and kubernetes nodes will mysteriously fail as a result. - - -Despite this being [known as problematic](https://twitter.com/codinghorror/status/604096348682485760) for some time and [documented](https://access.redhat.com/documentation/en/red-hat-enterprise-linux-atomic-host/7/single/getting-started-with-containers/#overlay_graph_driver), people still frequently [run into this](https://forums.docker.com/t/docker-with-devicemapper-doesnt-start-on-centos-7/9641) problem. - -Incidentally `yum install docker` can result in slightly different versions of docker installed. - -> Each docker release has some known issues running in Kubernetes as a runtime. - -[So what's the recommended docker version](https://github.com/kubernetes/kubernetes/issues/25893 )? v1.12 or v1.11? It turns out the latest (v1.12) is not yet supported by Kubernetes v1.4. - -_The problem is a distribution like CentOS 7, officially supported by Kubernetes, by default will arbitrarily work for some but not others, with the full requirements hidden and **underspecified**._ - -At the very least Docker versions should be pinned together with OS and Kubernetes versions and a recommendation about the storage driver should be made. - - -#### To avoid these pitfalls, carefully select the storage driver - -As Docker has a [pluggable storage driver architecture](https://docs.docker.com/engine/userguide/storagedriver/selectadriver/) and the default is (or might be) inappropriate, you must carefully consider your options. As discussed, getting this wrong will eventually cascade all the way to hard to debug and reproduce bugs and broken clusters. - ->Which storage driver should you choose? -Several factors influence the selection of a storage driver. However, these two facts must be kept in mind: - -> - No single driver is well suited to every use-case -> - Storage drivers are improving and evolving all of the time - -The docker docs don't take a position either. If one doesn't want to make assumptions about how many disks a machine has (laptops, bare metal servers with one drive, 'etc) direct LVM is out. - -AUFS [was the original backend](http://jpetazzo.github.io/assets/2015-03-03-not-so-deep-dive-into-docker-storage-drivers.html#28) used by docker but is not in the mainline kernel (it is however included by debian/ubuntu). - -Overlay is in mainline and supported as a Technology Preview by RHEL. - -Additionally _"Many people consider OverlayFS as the future of the Docker storage driver"_. It is the future proof way to go. - -##### Overlay Dependencies - -- CentOS 7.2 -- _"Only XFS is currently supported for use as a lower layer file system."_ -- _"/etc/sysconfig/docker must not contain --selinux-enabled"_ (for now) - -With the above satisfied, to enable overlay simply: - -`echo "overlay" > /etc/modules-load.d/overlay.conf` - -And add the flag (`--storage-driver=overlay`) in the docker service file or DOCKER_OPTS (`/etc/default/docker`). - -This requires a reboot, but first... - -### Properly configure netfilter - -`docker info` had another complaint. - -``` -WARNING: bridge-nf-call-iptables is disabled -WARNING: bridge-nf-call-ip6tables is disabled -``` - -This toggles whether packets traversing the bridge are forwarded to iptables. -This is [docker issue #24809](https://github.com/docker/docker/issues/24809) and _could_ be ignored ("either /proc/sys/net/bridge/bridge-nf-call-iptables doesn't exist or is set to 0"). CentOS and most distros default this to 0. - -If I was writing a choose your own adventure book this is the point I'd write that thunder rumbles in the distance, a quiet intensity. - -If you follow _this_ dead end all the way to a Kubernetes cluster you will find out that **kube-proxy requires that bridged traffic passes through netfilter**. So that path should absolutely exist otherwise you have a problem. - -Furthermore you'll find that [kube-proxy will not work properly with Weave on Centos](https://github.com/kubernetes/kubernetes/issues/33790) if this isn't toggled to 1. At first everything will appear to be fine, the problem only manifests itself by kubernetes service endpoints not being routable. - -To get rid of these warnings you might try: - -`echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables` - -`echo 1 > /proc/sys/net/bridge/bridge-nf-call-ip6tables` - -This would toggle the setting but not persist after a reboot. Once again, this will cause a situation where a cluster will initially appear to work fine. - -The above settings used to live in /etc/sysctl.conf the contents of which nowadays are: - -``` -# System default settings live in /usr/lib/sysctl.d/00-system.conf. -# To override those settings, enter new settings here, or in an /etc/sysctl.d/.conf file -``` - -This file is sourced on every invocation of `sysctl -p`. - -Attempting to toggle via `sysctl -p` [gives the following error](https://github.com/ansible/ansible/issues/6272) under certain conditions: - -``` -error: "net.bridge.bridge-nf-call-ip6tables" is an unknown key -error: "net.bridge.bridge-nf-call-iptables" is an unknown key -``` - -Since `sysctl` runs at boot there's also a very possible race condition [if the bridge module hasn't loaded yet](https://bugzilla.redhat.com/show_bug.cgi?id=1054178#c1) at that point. Making this a (sometimes) misleading error message. - -The correct way to set this as of CentOS7: - -> $ cat /usr/lib/sysctl.d/00-system.conf - -``` - -# Kernel sysctl configuration file -# -# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and -# sysctl.conf(5) for more details. - -# Disable netfilter on bridges. -net.bridge.bridge-nf-call-ip6tables = 0 -net.bridge.bridge-nf-call-iptables = 0 -net.bridge.bridge-nf-call-arptables = 0 - -``` - -> $ cat /usr/lib/sysctl.d/90-system.conf - -``` -net.bridge.bridge-nf-call-ip6tables = 1 -net.bridge.bridge-nf-call-iptables = 1 -``` - -This way systemd ensures these settings will be evaluated whenever a bridge module is loaded and the race condition is avoided. - -Speaking of misleading error messages, kubernetes logs an [incorrect br-netfilter warning on Centos 7](https://github.com/kubernetes/kubernetes/issues/23385): - -> proxier.go:205] missing br-netfilter module or unset br-nf-call-iptables; proxy may not work as intended - -Stay the course, there's nothing else to toggle to make this warning go away, it is simply a false positive. - -### Consider disabling selinux - -With Overlay as the storage backend currently you can only run with selinux on the host, a temporary limitation. - -However, elsewhere, kubernetes uses a mechanism that injects special volumes into each container to expose service account tokens and [with selinux turned on secrets simply don't work](http://stackoverflow.com/questions/35338213/kubernetes-serviceaccounts-and-selinux/35347520#35347520). - -The work around is to set the security context of volume on the kubernetes host (`sudo chcon -Rt svirt_sandbox_file_t /var/lib/kubelet`) or set selinux to permissive mode. - -Otherwise down the line [kubernetes add-ons](https://github.com/kubernetes/kubernetes/tree/master/cluster/addons) will fail or behave unpredictably. For example KubeDNS will fail to authenticate with the master and [dns lookups on service endpoints will fail](https://github.com/cncf/demo/issues/103). (Slightly differs from the bridge netfilter disabled problem described above which results in routing by ip intermittently failing) - -Since there might be other selinux permissions necessary elsewhere consider turning off selinux entirely until this is properly decided upon and documented. - -### Correct CNI config - - -Kubernetes supports [CNI Network Plugins](http://kubernetes.io/docs/admin/network-plugins/#cni) for interoperability. Setting up a network overlay requires this dependency. - -Kubernetes 1.3.5 [broke the cni config](https://github.com/kubernetes/kubernetes/issues/30681) — as of that version it is necessary to pull in the [cni release binaries](https://github.com/containernetworking/cni/releases) into the cni bin folder. - -As of Kuberentes 1.4 the [flags to specify cni directories](https://github.com/kubernetes/kubernetes.github.io/pull/1516) changed and documentation was added pinning the minimum cni version to 0.2 and at least the `lo` binary. - -### Other Dependencies - -There's [additional undocumented missing dependencies](https://github.com/cncf/demo/issues/64) as follows: - - - conntrack-tools - - socat - - bridge-utils - -### AWS specific requirements & debugging - -[Peeking under the hood of Kubernetes on AWS](https://github.com/kubernetes/kubernetes/blob/master/docs/design/aws_under_the_hood.md#tagging) you'll find: - -> All AWS resources are tagged with a tag named "KubernetesCluster", with a value that is the unique cluster-id. This tag is used to identify a particular 'instance' of Kubernetes, even if two clusters are deployed into the same VPC. Resources are considered to belong to the same cluster if and only if they have the same value in the tag named "KubernetesCluster". - -This isn't only necessary to differentiate resources between different clusters in the same VPC but also needed for the controller to discover and manage AWS resources at all (even if it has an entire VPC to itself). - -Unfortunately these tags are [not filtered on in a uniform manner across different resource types](https://github.com/cncf/demo/issues/144). - -A `kubectl create -f resource.yaml` successfully submitted to kubernetes might not result in expected functionality (in this case a load balancer endpoint) even when the desired resource shows as `creating...`. It will simply show that indefinitely instead of an error. - -Since the problem doesn't bubble up to kubectl responses the only way to see that something is amiss is by carefully watching the controller log. - -``` -aws.go:2731] Error opening ingress rules for the load balancer to the instances: Multiple tagged security groups found for instance i-04bd9c4c8aa; ensure only the k8s security group is tagged -``` - -[Reading the code](https://github.com/kubernetes/kubernetes/blob/master/pkg/cloudprovider/providers/aws/aws.go#L2783) yields: - -``` -// Returns the first security group for an instance, or nil -// We only create instances with one security group, so we don't expect multiple security groups. -// However, if there are multiple security groups, we will choose the one tagged with our cluster filter. -// Otherwise we will return an error. -``` - -In this example the kubernetes masters and minions each have a security group, both security groups are tagged with "KubernetesCluster=name". Removing the tags from the master security group resolves this problem as now the controller receives an expected response from the AWS API. It is easy to imagine many other scenarios where such conflicts might arise if the tag filtering is not consistent. - -Smoke tests that simply launch and destroy a large amount of pods and resources would not catch this problem either. - -## Conclusion - -The most difficult bugs are the ones that occur far away from their origins. -Bugs that will slowly but surely degrade a cluster and yet sneak past continuous integration tests. - -Additionally the target is a moving one. Minor releases of kubernetes can still have undocumented changes and undocumented dependencies. - -If a [critical Add-Ons fails](https://github.com/kubernetes/kubernetes/issues/14232) seemingly identical clusters deployed minutes apart will have divergent behaviour. The cloud environments clusters slot into are also a source of state and therfore subtle edgecase that can confuse the controller and silently prevent it from deploying things. - -In short, this is a complex support matrix. - -A possible way to improve things is by introducing: - -- A set of host OS images with minimal changes baked in as neccessary for Kubernetes - - - Continuously (weekly?) rebased on top of the latest official images - - - - As the basis for a well documented reference implementation of a custom cluster - -- Long running custom clusters spun up for each permutation of minor version updates (kubernetes version bump, weave, flannel, etcd, and so on) - -- A deterministic demo app/deployment as a comprehensive smoketest & benchmark - - -The community need to mix and match the multiple supported components with arbitrary neccessary for custom deployments can be benefit from a set of "blessed" kubernetes-flavored host OS images and a more typical real-world artifcat to check their customizations against. \ No newline at end of file diff --git a/docs/guid_from_oauth_endpoint.png b/docs/guid_from_oauth_endpoint.png deleted file mode 100644 index 310f5cba9ee4db3e7b4fc703be5b44fc6c7fc1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51214 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

Qs-q&W2*lM#)5R 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 UbRz;FEd(O>ToGC%`ReWe1MG#w6951J diff --git a/docs/k8s-cube.png b/docs/k8s-cube.png deleted file mode 100644 index a483e64a5c264977c3ebc12bdc14e1155cdce83c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29569 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 n|MTU4MrPnp#yI7bo#>VdmSy&{932u)NW= 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

qD^J_VZ={HOFXk{%@MQ5`2 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

3c0-za(iwu8w+nB=&W@AWcBd)sTDP}yP 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 diff --git a/docs/research.md b/docs/research.md deleted file mode 100644 index 4bf6085..0000000 --- a/docs/research.md +++ /dev/null @@ -1,61 +0,0 @@ -- [Azure Equivalence for Existing EC2](#sec-1) - - [Locating the Equivalent CentOS Image](#sec-1-1) -- [Summary](#sec-2) - -The [Terraform Azure Provider](https://www.terraform.io/docs/providers/azure/) needs a publishsettings file - -# Azure Equivalence for Existing EC2 - -## Locating the Equivalent CentOS Image - -Currently the cncf/demo [uses the latest CentOS7 AMI](https://github.com/cncf/demo/commit/62b4ee750cea96ac18d9998cebed36660b3d5864#diff-165521d9e758a5a743ea42c6fd528156R10), so let's go find that at Azure. - -```shell -az vm image list -o table -``` - -| Offer | Publisher | Sku | Urn | UrnAlias | Version | -| ------------- | ---------------------- | ------------------ | -------------------------------------------------------------- | ------------------- | --------- | -| WindowsServer | MicrosoftWindowsServer | 2016-Datacenter | MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest | Win2016Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-R2-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest | Win2012R2Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2008-R2-SP1 | MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest | Win2008R2SP1 | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest | Win2012Datacenter | latest | -| UbuntuServer | Canonical | 14.04.4-LTS | Canonical:UbuntuServer:14.04.4-LTS:latest | UbuntuLTS | latest | -| CentOS | OpenLogic | 7.2 | OpenLogic:CentOS:7.2:latest | CentOS | latest | -| openSUSE | SUSE | 13.2 | SUSE:openSUSE:13.2:latest | openSUSE | latest | -| RHEL | RedHat | 7.2 | RedHat:RHEL:7.2:latest | RHEL | latest | -| SLES | SUSE | 12-SP1 | SUSE:SLES:12-SP1:latest | SLES | latest | -| Debian | credativ | 8 | credativ:Debian:8:latest | Debian | latest | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:latest | CoreOS | latest | - -We are looking for CentOS, and looks like the default Publisher is OpenLogic. Let's see what SKU's they have in westus. - -```shell -az vm image list-skus -o table --publisher OpenLogic --location westus --offer CentOS -``` - -| Location | Name | -| ---------- | ------ | -| westus | 6.5 | -| westus | 6.6 | -| westus | 6.7 | -| westus | 6.8 | -| westus | 7.0 | -| westus | 7.1 | -| westus | 7.2 | -| westus | 7.2n | -| westus | 7.3 | - -I prefer not to use 'latest' anywhere, let's grab the precise version of CentOS 7.3. - -```shell -az vm image list --offer centos -o table --publisher openlogic --sku 7.3 --all -``` - -| Offer | Publisher | Sku | Urn | Version | -| ------- | ----------- | ----- | --------------------------------- | ------------ | -| CentOS | OpenLogic | 7.3 | OpenLogic:CentOS:7.3:7.3.20161221 | 7.3.20161221 | - -I'm comfortable using this image as our base. - -# Summary diff --git a/docs/research.org b/docs/research.org deleted file mode 100644 index a5b2eae..0000000 --- a/docs/research.org +++ /dev/null @@ -1,281 +0,0 @@ -# -*- org-use-property-inheritance: t; -*- -#+TITLE: Azure Support -#+AUTHOR: Hippie Hacker -#+EMAIL: hh@ii.coop -#+CREATOR: ii.coop -#+DATE: March 1st, 2017 -#+PROPERTY: header-args :dir ".." -#+NOTPROPERTY: header-args:shell :prologue ". .env_prod ; . ~/.rvm/scripts/rvm" -#+PROPERTY: header-args:shell :session none :exports both :cache yes - -* Azure Equivalence for Existing EC2 -Eventually we will -** Locating the Equivalent CentOS Image - -Currently the cncf/demo [[https://github.com/cncf/demo/commit/62b4ee750cea96ac18d9998cebed36660b3d5864#diff-165521d9e758a5a743ea42c6fd528156R10][uses the latest CentOS7 AMI]], so let's go find that at Azure. - -#+NAME:list azure default images -#+BEGIN_SRC shell -az vm image list -o table -#+END_SRC - -#+RESULTS[f42a28cef38b0b7c3346005ad6b0128d06d069b1]: list azure default images -| Offer | Publisher | Sku | Urn | UrnAlias | Version | -| ------------- | ---------------------- | ------------------ | -------------------------------------------------------------- | ------------------- | --------- | -| WindowsServer | MicrosoftWindowsServer | 2016-Datacenter | MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest | Win2016Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-R2-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest | Win2012R2Datacenter | latest | -| WindowsServer | MicrosoftWindowsServer | 2008-R2-SP1 | MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest | Win2008R2SP1 | latest | -| WindowsServer | MicrosoftWindowsServer | 2012-Datacenter | MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest | Win2012Datacenter | latest | -| UbuntuServer | Canonical | 14.04.4-LTS | Canonical:UbuntuServer:14.04.4-LTS:latest | UbuntuLTS | latest | -| CentOS | OpenLogic | 7.2 | OpenLogic:CentOS:7.2:latest | CentOS | latest | -| openSUSE | SUSE | 13.2 | SUSE:openSUSE:13.2:latest | openSUSE | latest | -| RHEL | RedHat | 7.2 | RedHat:RHEL:7.2:latest | RHEL | latest | -| SLES | SUSE | 12-SP1 | SUSE:SLES:12-SP1:latest | SLES | latest | -| Debian | credativ | 8 | credativ:Debian:8:latest | Debian | latest | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:latest | CoreOS | latest | - -We are looking for CentOS, and looks like the default Publisher is OpenLogic. Let's see what SKU's they have in westus. - -#+NAME: list OpenLogic CentOS sku offers -#+BEGIN_SRC shell -az vm image list-skus -o table --publisher OpenLogic --location westus --offer CentOS -#+END_SRC - -#+RESULTS[bd6ed1e6f2fc3c614c736373beafd146068045f4]: list OpenLogic CentOS sku offers -| Location | Name | -| ---------- | ------ | -| westus | 6.5 | -| westus | 6.6 | -| westus | 6.7 | -| westus | 6.8 | -| westus | 7.0 | -| westus | 7.1 | -| westus | 7.2 | -| westus | 7.2n | -| westus | 7.3 | - -I prefer not to use 'latest' anywhere, let's grab the precise version of CentOS 7.3. - -#+NAME: list OpenLogic CentOS 7.3 Versions -#+BEGIN_SRC shell -az vm image list --offer centos -o table --publisher openlogic --sku 7.3 --all -#+END_SRC - -#+RESULTS[7a0228f7fd750965697c45a561145ee0cb58e207]: list OpenLogic CentOS 7.3 Versions -| Offer | Publisher | Sku | Urn | Version | -| ------- | ----------- | ----- | --------------------------------- | ------------ | -| CentOS | OpenLogic | 7.3 | OpenLogic:CentOS:7.3:7.3.20161221 | 7.3.20161221 | - -I'm comfortable using this image as our base. - -** -* Communicating to Azure from Terraform - -The [[https://github.com/Azure/azure-cli][Azure-CLI]] can be utilized to create creds for terraform. - -Let's [[https://azure.microsoft.com/en-us/blog/run-azure-cli-as-a-docker-container-avoid-installation-and-setup/][run Azure CLI as a Docker Container]] -Be sure you have a Paid account, the free trail quotas are too low. - -#+NAME: azure account list -#+BEGIN_SRC shell -$ docker run -v $(pwd)/.azure:/root/.azure -it azuresdk/azure-cli-python az login -To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GLKE7GL6F to authenticate. -[ - { - "cloudName": "AzureCloud", - "id": "5358e673-95e7-4cd8-9791-ca28dd5e3cbb", - "isDefault": true, - "name": "Free Trial", - "state": "Enabled", - "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", - "user": { - "name": "azure@ii.coop", - "type": "user" - } - }, - { - "cloudName": "AzureCloud", - "id": "70693672-7c0d-485f-ac08-06d458c80f0e", - "isDefault": false, - "name": "Pay-As-You-Go", - "state": "Enabled", - "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", - "user": { - "name": "azure@ii.coop", - "type": "user" - } - } -] -#+END_SRC - -#+NAME: az account list -#+BEGIN_SRC shell -#az account list -#echo HELLO -# docker run -v $(pwd)/.azure:/root/.azure -it azuresdk/azure-cli-python az account list | jq ".[] | select(.name==\"Pay-As-You-Go\") | .id" -#+END_SRC - -#+RESULTS[4e9dccc24c6d7d7b761fe504e1cc41f7d41c7914]: az account list - -#+NAME: generate azure creds -#+BEGIN_SRC shell -$ docker run -v $(pwd)/.azure-cli-creds:/root -it azuresdk/azure-cli-python az login -To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GLKE7GL6F to authenticate. -[ - { - "cloudName": "AzureCloud", - "id": "5358e673-95e7-4cd8-9791-ca28dd5e3cbb", - "isDefault": true, - "name": "Free Trial", - "state": "Enabled", - "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", - "user": { - "name": "azure@ii.coop", - "type": "user" - } - } -] -#+END_SRC - -Please note that to run this demo full, you'll need to have a pay-as-you-go account due to a resource limits. - -#+BEGIN_SRC shell -ARM_SUBSCRIPTION_ID=$(az account list | jq '. | map([.id] | join("\n")) | join("\n")' | sed 's/"//g') -az account set --subscription="${ARM_SUBSCRIPTION_ID}" -az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${ARM_SUBSCRIPTION_ID}" | tee servicePrincipalProfile.json -ARM_CLIENT_ID=$(jq -r .appId servicePrincipalProfile.json) -ARM_CLIENT_SECRET=$(jq -r .password servicePrincipalProfile.json) -ARM_TENANT_ID=$(jq -r .tenant servicePrincipalProfile.json) -#+END_SRC - -#+NAME: list my lb -#+BEGIN_SRC shell :format js -az network lb list --output table -#+END_SRC - -#+RESULTS[50522849401e494e714a1f3cb413e3a936dd0cb3]: list my lb -| Location | Name | ProvisioningState | ResourceGroup | ResourceGuid | -| ---------- | ---------------- | ------------------- | --------------- | ------------------------------------ | -| westus | TestLoadBalancer | Succeeded | deploy | 93247839-cdcd-4d24-b461-acafafc59833 | - -#+NAME: my my lb -#+BEGIN_SRC shell :format js -az network lb show --name TestLoadBalancer --resource-group test -#az network lb show TestLoadBalancer -#+END_SRC - -#+NAME: list resource group -#+BEGIN_SRC shell -az group list -o table -#+END_SRC - -#+RESULTS[e1c878abc4d0ba479f12a7c7ae192f70a58feec1]: list resource group -| Name | Location | Status | -| ------ | ---------- | --------- | -| deploy | westus | Succeeded | - -#+NAME: my my lb -#+BEGIN_SRC shell :format js -az network lb show --name TestLoadBalancer --resource-group deploy -o table -#az network lb show TestLoadBalancer -#+END_SRC - -#+RESULTS[75c3b3395ca9f70185e880e4c9b14338b7287472]: my my lb -| Location | Name | ProvisioningState | ResourceGroup | ResourceGuid | -| ---------- | ---------------- | ------------------- | --------------- | ------------------------------------ | -| westus | TestLoadBalancer | Succeeded | deploy | 93247839-cdcd-4d24-b461-acafafc59833 | - - -#+BEGIN_SRC - -openssl x509 -in .cfssl/k8s-admin.pem -noout -text -openssl x509 -in .cfssl/k8s-apiserver.pem -noout -text - - -#+END_SRC - -#+BEGIN_SRC shell -az group delete --name deploy --yes -#+END_SRC - -** CoreOS - -We might start with CoreOS to focus on getting cloud up now, then look at images. - -#+NAME: list CoreOS sku offers -#+BEGIN_SRC shell -az vm image list-skus -o table --publisher CoreOS --location westus --offer CoreOS -#+END_SRC - -#+RESULTS[1756d18b49a91fd7d620f538082e7101545e22d7]: list CoreOS sku offers -| Location | Name | -| ---------- | ------ | -| westus | Alpha | -| westus | Beta | -| westus | Stable | - -#+NAME: list CoreOS Stable Versions -#+BEGIN_SRC shell -az vm image list --offer CoreOS -o table --publisher CoreOS --sku Stable --all -#+END_SRC - -#+RESULTS[508f2fcde4974eced346be39c6788c31c21e5230]: list CoreOS Stable Versions -| Offer | Publisher | Sku | Urn | Version | -| ------- | ----------- | ------ | ------------------------------ | --------- | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1010.5.0 | 1010.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1010.6.0 | 1010.6.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1068.6.0 | 1068.6.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1068.8.0 | 1068.8.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1068.9.0 | 1068.9.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1122.2.0 | 1122.2.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1122.3.0 | 1122.3.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1185.3.0 | 1185.3.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1185.5.0 | 1185.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.12.0 | 1235.12.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.5.0 | 1235.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.6.0 | 1235.6.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.8.0 | 1235.8.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1235.9.0 | 1235.9.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1298.5.0 | 1298.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1298.6.0 | 1298.6.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:494.3.0 | 494.3.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:494.4.0 | 494.4.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:494.5.0 | 494.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:522.5.0 | 522.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:522.6.0 | 522.6.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:557.2.0 | 557.2.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:607.0.0 | 607.0.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:633.1.0 | 633.1.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:647.0.0 | 647.0.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:647.2.0 | 647.2.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:681.0.0 | 681.0.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:681.1.0 | 681.1.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:681.2.0 | 681.2.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:717.1.0 | 717.1.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:717.3.0 | 717.3.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:723.3.0 | 723.3.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:766.3.0 | 766.3.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:766.4.0 | 766.4.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:766.5.0 | 766.5.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.10.0 | 835.10.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.11.0 | 835.11.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.12.0 | 835.12.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.13.0 | 835.13.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.8.0 | 835.8.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:835.9.0 | 835.9.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:899.13.0 | 899.13.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:899.15.0 | 899.15.0 | -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:899.17.0 | 899.17.0 | - -I think we want: - -| CoreOS | CoreOS | Stable | CoreOS:CoreOS:Stable:1298.5.0 | 1298.5.0 | - -* Summary - -# Local Variables: -# eval: (require (quote ob-shell)) -# eval: (require (quote ob-lisp)) -# eval: (org-babel-do-load-languages 'org-babel-load-languages '((js . t) (shell . t))) -# eval: (setenv "PATH" (concat (concat (getenv "HOME") "/bin:") (getenv "PATH") )) -# End: diff --git a/docs/sdn.png b/docs/sdn.png deleted file mode 100644 index 9c0348401182dc9b1a02b0172217027bf1ce515d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59817 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 diff --git a/docs/src/k8s-simpler.graffle b/docs/src/k8s-simpler.graffle deleted file mode 100644 index c5041cd90c8900df9e930dface9822925d5df078..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2907 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 F001^7r+EMX diff --git a/docs/src/sdn.graffle b/docs/src/sdn.graffle deleted file mode 100644 index 3d8d29ddad95903793940f240b6461305df71655..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3321 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 D_=9oS diff --git a/docs/web_api_application_type.png b/docs/web_api_application_type.png deleted file mode 100644 index 3fdfae5ff7539bec9d055da5dd58afdf03708557..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34235 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 diff --git a/cert.tf b/gce/cert.tf similarity index 100% rename from cert.tf rename to gce/cert.tf diff --git a/cloud-config.tf b/gce/cloud-config.tf similarity index 100% rename from cloud-config.tf rename to gce/cloud-config.tf diff --git a/gce.tf b/gce/gce.tf similarity index 100% rename from gce.tf rename to gce/gce.tf diff --git a/init-cfssl b/gce/init-cfssl similarity index 100% rename from init-cfssl rename to gce/init-cfssl diff --git a/input.tf b/gce/input.tf similarity index 100% rename from input.tf rename to gce/input.tf diff --git a/keypair.tf b/gce/keypair.tf similarity index 100% rename from keypair.tf rename to gce/keypair.tf diff --git a/modules.tf b/gce/modules.tf similarity index 100% rename from modules.tf rename to gce/modules.tf diff --git a/modules/bastion/input.tf b/gce/modules/bastion/input.tf similarity index 100% rename from modules/bastion/input.tf rename to gce/modules/bastion/input.tf diff --git a/modules/bastion/node.tf b/gce/modules/bastion/node.tf similarity index 100% rename from modules/bastion/node.tf rename to gce/modules/bastion/node.tf diff --git a/modules/bastion/output.tf b/gce/modules/bastion/output.tf similarity index 100% rename from modules/bastion/output.tf rename to gce/modules/bastion/output.tf diff --git a/modules/bastion/user-data.yml b/gce/modules/bastion/user-data.yml similarity index 100% rename from modules/bastion/user-data.yml rename to gce/modules/bastion/user-data.yml diff --git a/modules/dns/dns.tf b/gce/modules/dns/dns.tf similarity index 100% rename from modules/dns/dns.tf rename to gce/modules/dns/dns.tf diff --git a/modules/dns/input.tf b/gce/modules/dns/input.tf similarity index 100% rename from modules/dns/input.tf rename to gce/modules/dns/input.tf diff --git a/modules/dns/output.tf b/gce/modules/dns/output.tf similarity index 100% rename from modules/dns/output.tf rename to gce/modules/dns/output.tf diff --git a/modules/etcd/cloud-config.tf b/gce/modules/etcd/cloud-config.tf similarity index 100% rename from modules/etcd/cloud-config.tf rename to gce/modules/etcd/cloud-config.tf diff --git a/modules/etcd/cloud-config.yml b/gce/modules/etcd/cloud-config.yml similarity index 100% rename from modules/etcd/cloud-config.yml rename to gce/modules/etcd/cloud-config.yml diff --git a/modules/etcd/discovery.tf b/gce/modules/etcd/discovery.tf similarity index 100% rename from modules/etcd/discovery.tf rename to gce/modules/etcd/discovery.tf diff --git a/modules/etcd/external_lb.tf b/gce/modules/etcd/external_lb.tf similarity index 100% rename from modules/etcd/external_lb.tf rename to gce/modules/etcd/external_lb.tf diff --git a/modules/etcd/input.tf b/gce/modules/etcd/input.tf similarity index 100% rename from modules/etcd/input.tf rename to gce/modules/etcd/input.tf diff --git a/modules/etcd/internal_lb.tf b/gce/modules/etcd/internal_lb.tf similarity index 100% rename from modules/etcd/internal_lb.tf rename to gce/modules/etcd/internal_lb.tf diff --git a/modules/etcd/load-balancer.tf b/gce/modules/etcd/load-balancer.tf similarity index 100% rename from modules/etcd/load-balancer.tf rename to gce/modules/etcd/load-balancer.tf diff --git a/modules/etcd/nodes.tf b/gce/modules/etcd/nodes.tf similarity index 100% rename from modules/etcd/nodes.tf rename to gce/modules/etcd/nodes.tf diff --git a/modules/etcd/output.tf b/gce/modules/etcd/output.tf similarity index 100% rename from modules/etcd/output.tf rename to gce/modules/etcd/output.tf diff --git a/modules/kubeconfig/io.tf b/gce/modules/kubeconfig/io.tf similarity index 100% rename from modules/kubeconfig/io.tf rename to gce/modules/kubeconfig/io.tf diff --git a/modules/kubeconfig/kubeconfig.tf b/gce/modules/kubeconfig/kubeconfig.tf similarity index 100% rename from modules/kubeconfig/kubeconfig.tf rename to gce/modules/kubeconfig/kubeconfig.tf diff --git a/modules/security/io.tf b/gce/modules/security/io.tf similarity index 100% rename from modules/security/io.tf rename to gce/modules/security/io.tf diff --git a/modules/security/security.tf b/gce/modules/security/security.tf similarity index 100% rename from modules/security/security.tf rename to gce/modules/security/security.tf diff --git a/modules/vpc/azure-security.tf b/gce/modules/vpc/azure-security.tf similarity index 100% rename from modules/vpc/azure-security.tf rename to gce/modules/vpc/azure-security.tf diff --git a/modules/vpc/gce-subnet.tf b/gce/modules/vpc/gce-subnet.tf similarity index 100% rename from modules/vpc/gce-subnet.tf rename to gce/modules/vpc/gce-subnet.tf diff --git a/modules/vpc/io.tf b/gce/modules/vpc/io.tf similarity index 100% rename from modules/vpc/io.tf rename to gce/modules/vpc/io.tf diff --git a/modules/vpc/output.tf b/gce/modules/vpc/output.tf similarity index 100% rename from modules/vpc/output.tf rename to gce/modules/vpc/output.tf diff --git a/modules/vpc/private.tf b/gce/modules/vpc/private.tf similarity index 100% rename from modules/vpc/private.tf rename to gce/modules/vpc/private.tf diff --git a/modules/vpc/public.tf b/gce/modules/vpc/public.tf similarity index 100% rename from modules/vpc/public.tf rename to gce/modules/vpc/public.tf diff --git a/modules/vpc/vpc.tf b/gce/modules/vpc/vpc.tf similarity index 100% rename from modules/vpc/vpc.tf rename to gce/modules/vpc/vpc.tf diff --git a/modules/worker/cloud-config.tf b/gce/modules/worker/cloud-config.tf similarity index 100% rename from modules/worker/cloud-config.tf rename to gce/modules/worker/cloud-config.tf diff --git a/modules/worker/cloud-config.yml b/gce/modules/worker/cloud-config.yml similarity index 100% rename from modules/worker/cloud-config.yml rename to gce/modules/worker/cloud-config.yml diff --git a/modules/worker/input.tf b/gce/modules/worker/input.tf similarity index 100% rename from modules/worker/input.tf rename to gce/modules/worker/input.tf diff --git a/modules/worker/nodes.tf b/gce/modules/worker/nodes.tf similarity index 100% rename from modules/worker/nodes.tf rename to gce/modules/worker/nodes.tf diff --git a/output.tf b/gce/output.tf similarity index 100% rename from output.tf rename to gce/output.tf diff --git a/runme b/gce/runme similarity index 100% rename from runme rename to gce/runme diff --git a/wait-for-cluster b/gce/wait-for-cluster similarity index 100% rename from wait-for-cluster rename to gce/wait-for-cluster diff --git a/modules/etcd/.#cloud-config.yml b/modules/etcd/.#cloud-config.yml deleted file mode 120000 index 546f459..0000000 --- a/modules/etcd/.#cloud-config.yml +++ /dev/null @@ -1 +0,0 @@ -dlx@W541.4550:1491707970 \ No newline at end of file diff --git a/readme.org b/readme.org deleted file mode 100644 index 4cec32b..0000000 --- a/readme.org +++ /dev/null @@ -1,211 +0,0 @@ -# -*- org-use-property-inheritance: t; -*- -#+TITLE: CNCF Demo on Azure -#+AUTHOR: Hippie Hacker -#+EMAIL: hh@ii.coop -#+CREATOR: ii.coop -#+DATE: March 1st, 2017 -#+PROPERTY: header-args :dir "." -#+NOTPROPERTY: header-args:shell :prologue ". .env_prod ; . ~/.rvm/scripts/rvm" -#+PROPERTY: header-args:shell :session none :exports both :cache yes -* tldr - -#+NAME: tldr -#+BEGIN_SRC shell -docker run -v $(pwd)/data:/data -ti generate/creds:azure -# this will ask you to launch a web browser to authenticate to azure -# it will result in ./data/azure.env -# deployname can only consist of lowercase letters and numbers, and must be less than 18 characters long -DEPLOYNAME=$(date +%Y%m%d%H%M) # could be a branch+gitcommit etc -# all data for the deploy will be stored in it's own directory -docker run -ti -v $(pwd)/data/$DEPLOYNAME:/cncf/data --env-file ./data/azure.env create/azure deploy $DEPLOYNAME -#+END_SRC - -* access your k8s cluster - -#+NAME: 3 minute deploy -#+BEGIN_SRC output -bastion-fqdn = bastion201703230324.westus.cloudapp.azure.com -fqdn-k8s = k8s201703230324.westus.cloudapp.azure.com -ssh-key-setup = eval $(ssh-agent) ; ssh-add /cncf/data/.ssh/id_rsa -ssh-via-bastion = ssh -At cncf@bastion201703230324.westus.cloudapp.azure.com ssh cncf@master1.cncf.demo - -real 3m2.509s -user 0m4.460s -sys 0m0.668s -# takes about 3-8 minutes to deploy - -sudo chown -R $(whoami):$(whoami) $(pwd)/data/${DEPLOYNAME} -export KUBECONFIG=$(pwd)/data/${DEPLOYNAME}/kubeconfig - -kubectl get nodes -NAME STATUS AGE -etcd-master1 Ready,SchedulingDisabled 13m -etcd-master2 Ready,SchedulingDisabled 13m -etcd-master3 Ready,SchedulingDisabled 14m -worker-node1 Ready 10m -worker-node2 Ready 13m -worker-node3 Ready 13m -... -kubectl proxy etc - -# to destroy -docker run -ti -v $(pwd)/data/$DEPLOYNAME:/cncf/data --env-file ./data/azure.env terminate/azure destroy $DEPLOYNAME -#+END_SRC - -Using DEPLOYNAME allows for multiple concurrent deploys and to easily. - -* Customing the Deploy via environment - -Adding var-name=value to ./data/terraform.tfvars will allow you to override many settings for this deploy. - -#+BEGIN_SRC shell -name= "azure" -location= "westus" -internal-tld= "cncf.demo" -master-node-count= "3" -worker-node-count= "3" -master-vm-size= "Standard_A2" -worker-vm-size= "Standard_A2" -bastion-vm-size= "Standard_A2" -kubelet-image-url= "quay.io/coreos/hyperkube" -kubelet-image-tag= "v1.4.7_coreos.0" -image-publisher= "CoreOS" -image-offer= "CoreOS" -image-sku= "Stable" -image-version= "1298.6.0" -#+END_SRC - -* Next steps - -- Store private keys in storage rather than cloud-init -- Look into using Centos and Debian/Ubuntu -- Deploy onto AWS and Azure at the same time -- Setup some CI to deploy on every commit to both clouds -- Start on GCE - -* Notable issues - -** Azure dns_zones do not provide IPs -Specifically [[https://www.terraform.io/docs/providers/azurerm/r/dns_zone.html#name_servers][azurerm_dns_zone name_servers]] only provides the server names, while [[https://www.terraform.io/docs/providers/azurerm/r/network_interface.html#dns_servers][azurerm_network_interface requires a list of IPs]]. -We'll do a cleaner maping later, but the [[https://github.com/cncf/demo/pull/194/files#diff-8f1d08cae7f5b62ea7e23f2cb3b0b67bR7][current hack]] got us IPs and -** Azure CNAME records don't resolve correctly -We had a couple places where CNAMEs behaved unexpectedly when using Azure dns zones. -Specifically CNAME records when queried with DIG wouldn't refer. -** Terraform azurerm_dns_srv_records do not support multiple dynamic entries -This affects our ability to bootstrap etcd with an unknown number of nodes beforehand. -If we stick with three (or any number) it's not a problem. -** Azure Cluster-Autoscale Virtual Machine Scale Sets are not yet supported by kubernetes -We should be able to scale up our workers at some point [[https://github.com/Azure/ACS/blob/master/kubernetes-status.md#future-work][in the future]]. -** Starting kubelet without --cloud-config=azure.json results in a panic -When using --cloud-provider=azure not only must you use ---cloud-config=azure.json, it seems you have to provide all the optional -settings as well. Failure to do so results in a panic. -** Hostnames and VM names must match in order for kubelet to find instances -[[https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/kubelet_node_status.go#L255][instances are looked up via nodeName]] and if they don't match, kubelet will not start. - -#+BEGIN_SRC example -kubelet_node_status.go:69] Unable to construct api.Node object for kubelet: - failed to get external ID from cloud provider: instance not found -#+END_SRC - -* generate credentials -** via a container - -#+NAME: generate/creds:azure -#+BEGIN_SRC shell -$ docker run -v $(pwd)/data:/data -ti generate/creds:azure -To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GY7W7BMRZ to authenticate. -Name CloudName SubscriptionId State IsDefault -------------- ----------- ------------------------------------ -------- ----------- -Free Trial AzureCloud 5358e673-95e7-4cd8-9791-ca28dd5e3cbb Disabled True -Pay-As-You-Go AzureCloud 70693672-7c0d-485f-ac08-06d458c80f0e Enabled - -Please enter the Name of the account you wish to use. If you do not see -a valid account in the list press Ctrl+C to abort and create one. -If you leave this blank we will use the Current account. -> Pay-As-You-Go -Using subscription_id: 70693672-7c0d-485f-ac08-06d458c80f0e -Using tenant_id: 9996322a-93ac-43ae-80be-887a3e8194a1 -==> Creating service principal -Retrying role assignment creation: 1/36 -Retrying role assignment creation: 2/36 -./data/azure.env created -$ cat ./data/azure.env -export ARM_SUBSCRIPTION_ID=70693672-XXXX-4858-ac08-06888888880e -export ARM_TENANT_ID=9896828a-93ac-43ae-YYYY-887a3e8898a1 -export ARM_CLIENT_ID=968448ae-f9f9-ZZZZ-bf43-5c081da88975 -export ARM_CLIENT_SECRET=BBBBBBBB-8eaa-AAAA-aafe-75b02ad4ceba -#+END_SRC - -** manually - -#+NAME: run az via docker -#+BEGIN_SRC -docker run -v $(pwd)/.azure:/root/.azure azuresdk/azure-cli-python az account list -o table -#+END_SRC - -#+NAME: az account list -#+BEGIN_SRC shell -az account list -o table -az account set --subscription Pay-As-You-Go -#+END_SRC - -#+RESULTS[eb0d69eb1ea1b9a005604b3dd37889127d19f76b]: az account list -| Name | CloudName | SubscriptionId | State | IsDefault | -| ------------- | ----------- | ------------------------------------ | -------- | ----------- | -| Free | Trial | AzureCloud | 5358e673-95e7-4cd8-9791-ca28dd5e3cbb | Disabled | -| Pay-As-You-Go | AzureCloud | 70693672-7c0d-485f-ac08-06d458c80f0e | Enabled | True | - -#+NAME: az account show table -#+BEGIN_SRC shell :results output verbatim raw -az account show -o table -#+END_SRC - -#+RESULTS[00afff595364da643372e54234a45a775c1539ef]: az account show table -| EnvironmentName | IsDefault | Name | State | TenantId | -| ----------------- | ----------- | ------------- | ------- | ------------------------------------ | -| AzureCloud | True | Pay-As-You-Go | Enabled | 9996322a-93ac-43ae-80be-887a3e8194a1 | - -#+NAME: az_account_show_json -#+HEADERS: :wrap SRC js -#+HEADERS: :results output -#+BEGIN_SRC shell :export both -az account show -#+END_SRC - -#+RESULTS[97a6b7ba7839519d9223a4e67e27ced7ed78f0b9]: az_account_show_json -#+BEGIN_SRC js -{ - "environmentName": "AzureCloud", - "id": "70693672-7c0d-485f-ac08-06d458c80f0e", - "isDefault": true, - "name": "Pay-As-You-Go", - "state": "Enabled", - "tenantId": "9996322a-93ac-43ae-80be-887a3e8194a1", - "user": { - "name": "azure@ii.coop", - "type": "user" - } -} -#+END_SRC - -#+NAME: generate ENV -#+BEGIN_SRC shell -ARM_SUBSCRIPTION_ID=$( az account show | jq -r .id ) -CREDS_JSON=$( az ad sp create-for-rbac --name cncfdemos ) -ARM_TENANT_ID=$( echo ${CREDS_JSON} | jq -r .tenant ) -ARM_CLIENT_ID=$( echo ${CREDS_JSON} | jq -r .appId ) -ARM_CLIENT_SECRET=$( echo ${CREDS_JSON} | jq -r .password ) -echo export ARM_SUBSCRIPTION_ID=$ARM_SUBSCRIPTION_ID -echo export ARM_TENANT_ID=$ARM_TENANT_ID -echo export ARM_CLIENT_ID=$ARM_CLIENT_ID -echo export ARM_CLIENT_SECRET=$ARM_CLIENT_SECRET -#+END_SRC - -# Local Variables: -# eval: (require (quote ob-shell)) -# eval: (require (quote ob-lisp)) -# eval: (require (quote ob-js)) -# eval: (org-babel-do-load-languages 'org-babel-load-languages '((js . t) (shell . t))) -# eval: (setenv "PATH" (concat (concat (getenv "HOME") "/bin:") (getenv "PATH") )) -# End: From cd811ad821feb4f8eebef238d1fab33df4de71a5 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Fri, 21 Apr 2017 06:24:52 +1200 Subject: [PATCH 106/149] Add cross-cloud terraform code --- cross-cloud/cloud.tf | 20 ++++++++++++++ cross-cloud/input.tf | 26 ++++++++++++++++++ cross-cloud/output.tf | 8 ++++++ entrypoint.sh | 62 +++++++++++++++++++++++++------------------ gce/gce.tf | 2 +- 5 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 cross-cloud/cloud.tf create mode 100644 cross-cloud/input.tf create mode 100644 cross-cloud/output.tf diff --git a/cross-cloud/cloud.tf b/cross-cloud/cloud.tf new file mode 100644 index 0000000..faa5c4d --- /dev/null +++ b/cross-cloud/cloud.tf @@ -0,0 +1,20 @@ +module "aws" { + source = "../aws" + name = "${ var.name }-aws" + internal_tld = "${ var.name }-aws.cncf.demo" + data_dir = "${ var.data_dir }/aws" +} + +module "azure" { + source = "../azure" + name = "${ var.name }azure" + internal_tld = "${ var.name }-azure.cncf.demo" + data_dir = "${ var.data_dir }/azure" +} + +module "packet" { + source = "../packet" + name = "${ var.name }-packet" + data_dir = "${ var.data_dir }/packet" + packet_project_id = "${ var.packet_project_id }" +} diff --git a/cross-cloud/input.tf b/cross-cloud/input.tf new file mode 100644 index 0000000..93e9207 --- /dev/null +++ b/cross-cloud/input.tf @@ -0,0 +1,26 @@ +variable "name" { default = "ci" } + +# Set with env TF_VAR_packet_project_id +variable "packet_project_id" {} # required for now +variable "data_dir" { default = "/cncf/data/cross-cloud" } + +variable "domain" { default = "cncf.ci" } +#variable "data_dir" { default = "/cncf/data/packet" } + +# Kubernetes +variable "cluster_domain" { default = "cluster.local" } +variable "pod_cidr" { default = "10.2.0.0/16" } +variable "service_cidr" { default = "10.3.0.0/24" } +variable "k8s_service_ip" { default = "10.3.0.1" } +variable "dns_service_ip" { default = "10.3.0.10" } +variable "master_node_count" { default = "3" } +variable "worker_node_count" { default = "3" } +# For clouds that support autoscaling +variable "worker_node_min" { default = "3" } +variable "worker_node_max" { default = "5" } + +# Deployment Artifact Versions +# Hyperkube +# Set from https://quay.io/repository/coreos/hyperkube +variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} +variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} diff --git a/cross-cloud/output.tf b/cross-cloud/output.tf new file mode 100644 index 0000000..8377a4c --- /dev/null +++ b/cross-cloud/output.tf @@ -0,0 +1,8 @@ +# outputs +output "aws_external_elb" { value = "${ module.aws.external_elb }" } +output "aws_internal_tld" { value = "${ module.aws.internal_tld }" } +# standardizing key locations would be nice +output "ssh_key_setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir }/*/*.pem ; ssh-add ${ var.data_dir}/*/.ssh/id_rsa" } +output "aws_bastion" { value = "${ module.aws.ssh_via_bastion }" } +output "azure_k8s_fqdn" { value = "${ module.azure.fqdn_k8s }" } +output "azure_bastion" { value = "${ module.azure.ssh_via_bastion }" } diff --git a/entrypoint.sh b/entrypoint.sh index 0061397..d7e0d26 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -15,55 +15,65 @@ cd $TF_VAR_data_dir # Run CMD if [ "$1" = "aws-deploy" ] ; then - terraform get /build/aws && \ - terraform apply -target null_resource.ssl_gen /build/aws && \ + terraform get /cncf/aws && \ + terraform apply -target null_resource.ssl_gen /cncf/aws && \ time terraform apply /deploy/aws && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/ \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/kubeconfig \n\n'${NC} elif [ "$1" = "aws-destroy" ] ; then write_aws_config - time terraform destroy -force /build/aws + time terraform destroy -force /cncf/aws elif [ "$1" = "azure-deploy" ] ; then # There are some dependency issues around cert,sshkey,k8s_cloud_config, and dns # since they use files on disk that are created on the fly # should probably move these to data resources - terraform get /build/azure && \ - terraform apply -target null_resource.sshkey_gen /build/azure && \ - terraform apply -target null_resource.ssl_gen /build/azure && \ - terraform apply -target null_resource.cloud_gen /build/azure && \ - terraform apply -target module.dns.null_resource.dns_gen /build/azure && \ - terraform apply -target module.etcd.azurerm_network_interface.cncf /build/azure && \ - time terraform apply /build/azure && \ + terraform get /cncf/azure && \ + terraform apply -target null_resource.sshkey_gen /cncf/azure && \ + terraform apply -target null_resource.ssl_gen /cncf/azure && \ + terraform apply -target null_resource.cloud_gen /cncf/azure && \ + terraform apply -target module.dns.null_resource.dns_gen /cncf/azure && \ + terraform apply -target module.etcd.azurerm_network_interface.cncf /cncf/azure && \ + time terraform apply /cncf/azure && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "azure-destroy" ] ; then - time terraform destroy -force /build/azure + time terraform destroy -force /cncf/azure elif [ "$1" = "packet-deploy" ] ; then - terraform get /build/packet && \ - terraform apply -target module.etcd.null_resource.discovery_gen /build/packet && \ - terraform apply -target null_resource.ssl_ssh_gen /build/packet && \ - time terraform apply /build/packet && \ + terraform get /cncf/packet && \ + terraform apply -target module.etcd.null_resource.discovery_gen /cncf/packet && \ + terraform apply -target null_resource.ssl_ssh_gen /cncf/packet && \ + time terraform apply /cncf/packet && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "packet-destroy" ] ; then - time terraform destroy -force /build/packet + time terraform destroy -force /cncf/packet +elif [ "$1" = "gce-deploy" ] ; then + terraform get /cncf/gce && \ + terraform apply -target module.etcd.null_resource.discovery_gen /cncf/gce && \ + terraform apply -target null_resource.ssl_gen /cncf/gce && \ + time terraform apply /cncf/gce && \ + printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ + printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ + printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} +elif [ "$1" = "gce-destroy" ] ; then + time terraform destroy -force /cncf/gce elif [ "$1" = "cross-cloud-deploy" ] ; then - terraform get /build/cross-cloud && \ - terraform apply -target module.aws.null_resource.ssl_gen /build/cross-cloud && \ - terraform apply -target module.azure.azurerm_resource_group.cncf /build/cross-cloud && \ - terraform apply -target module.azure.null_resource.ssl_ssh_cloud_gen /build/cross-cloud && \ - terraform apply -target module.azure.module.dns.null_resource.dns_gen /build/cross-cloud && \ - terraform apply -target module.etcd.azurerm_network_interface.cncf /build/cross-cloud && \ - terraform apply -target module.packet.null_resource.ssl_ssh_gen /build/cross-cloud && \ - terraform apply -target module.packet.module.etcd.null_resource.discovery_gen /build/cross-cloud && \ - time terraform apply /build/cross-cloud && \ + terraform get /cncf/cross-cloud && \ + terraform apply -target module.aws.null_resource.ssl_gen /cncf/cross-cloud && \ + terraform apply -target module.azure.azurerm_resource_group.cncf /cncf/cross-cloud && \ + terraform apply -target module.azure.null_resource.ssl_ssh_cloud_gen /cncf/cross-cloud && \ + terraform apply -target module.azure.module.dns.null_resource.dns_gen /cncf/cross-cloud && \ + terraform apply -target module.etcd.azurerm_network_interface.cncf /cncf/cross-cloud && \ + terraform apply -target module.packet.null_resource.ssl_ssh_gen /cncf/cross-cloud && \ + terraform apply -target module.packet.module.etcd.null_resource.discovery_gen /cncf/cross-cloud && \ + time terraform apply /cncf/cross-cloud && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "cross-cloud-destroy" ] ; then - time terraform destroy -force /build/cross-cloud + time terraform destroy -force /cncf/cross-cloud fi diff --git a/gce/gce.tf b/gce/gce.tf index ec80299..eff33d1 100644 --- a/gce/gce.tf +++ b/gce/gce.tf @@ -1,6 +1,6 @@ # Configure the Microsoft Azure Provider provider "google" { - credentials = "${file("gce.json")}" + #credentials = "${file("gce.json")}" project = "${ var.project }" region = "${ var.region }" } From 5ccd1171c2250f13c7ee0ed025084440ec9ab194 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Fri, 21 Apr 2017 07:15:01 +1200 Subject: [PATCH 107/149] Use the same kubeconfig module for all clouds --- aws/modules.tf | 4 ++-- aws/modules/kubeconfig/io.tf | 7 ------ aws/modules/kubeconfig/kubeconfig.tf | 23 ------------------- azure/modules.tf | 2 +- azure/modules/kubeconfig/input.tf | 6 ----- azure/modules/kubeconfig/kubeconfig.tf | 23 ------------------- cross-cloud/cloud.tf | 30 +++++++++++++++++++++++++ packet/modules.tf | 2 +- packet/modules/kubeconfig/input.tf | 6 ----- packet/modules/kubeconfig/kubeconfig.tf | 23 ------------------- packet/output.tf | 2 +- 11 files changed, 35 insertions(+), 93 deletions(-) delete mode 100644 aws/modules/kubeconfig/io.tf delete mode 100644 aws/modules/kubeconfig/kubeconfig.tf delete mode 100644 azure/modules/kubeconfig/input.tf delete mode 100644 azure/modules/kubeconfig/kubeconfig.tf delete mode 100644 packet/modules/kubeconfig/input.tf delete mode 100644 packet/modules/kubeconfig/kubeconfig.tf diff --git a/aws/modules.tf b/aws/modules.tf index 6026f03..d39d214 100644 --- a/aws/modules.tf +++ b/aws/modules.tf @@ -109,12 +109,12 @@ module "worker" { } module "kubeconfig" { - source = "./modules/kubeconfig" + source = "../kubeconfig" admin_key_pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" admin_pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" ca_pem = "${ var.data_dir }/.cfssl/ca.pem" data_dir = "${ var.data_dir }" - master_elb = "${ module.etcd.external_elb }" + fqdn_k8s = "${ module.etcd.external_elb }" name = "${ var.name }" } diff --git a/aws/modules/kubeconfig/io.tf b/aws/modules/kubeconfig/io.tf deleted file mode 100644 index ff6ddc0..0000000 --- a/aws/modules/kubeconfig/io.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "admin_key_pem" {} -variable "admin_pem" {} -variable "ca_pem" {} -variable "master_elb" {} -variable "name" {} -variable "data_dir" {} - diff --git a/aws/modules/kubeconfig/kubeconfig.tf b/aws/modules/kubeconfig/kubeconfig.tf deleted file mode 100644 index ec3a5b0..0000000 --- a/aws/modules/kubeconfig/kubeconfig.tf +++ /dev/null @@ -1,23 +0,0 @@ -resource "null_resource" "kubeconfig" { - - provisioner "local-exec" { - command = < Date: Fri, 21 Apr 2017 10:10:43 +1200 Subject: [PATCH 108/149] combine kubeconfig output for all clouds --- aws/output.tf | 1 + azure/output.tf | 1 + cross-cloud/cloud.tf | 33 ++++++--------------------------- cross-cloud/output.tf | 1 + gce/output.tf | 1 + kubeconfig/input.tf | 6 ++++++ kubeconfig/kubeconfig.tf | 26 ++++++++++++++++++++++++++ kubeconfig/output.tf | 1 + packet/output.tf | 1 + 9 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 kubeconfig/input.tf create mode 100644 kubeconfig/kubeconfig.tf create mode 100644 kubeconfig/output.tf diff --git a/aws/output.tf b/aws/output.tf index 53e6069..92d2169 100644 --- a/aws/output.tf +++ b/aws/output.tf @@ -12,3 +12,4 @@ output "subnet_ids_public" { value = "${ module.vpc.subnet_ids_public }" } output "worker_autoscaling_group_name" { value = "${ module.worker.autoscaling_group_name }" } output "ssh_key_setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir}/${ var.name}.pem" } output "ssh_via_bastion" { value = "ssh -At ${ var.admin_username }@${ module.bastion.ip } ssh ${ var.admin_username }@etcd1.${ var.internal_tld }"} +output "kubeconfig" { value = "${ module.kubeconfig.kubeconfig }"} diff --git a/azure/output.tf b/azure/output.tf index f0b9715..8306c86 100644 --- a/azure/output.tf +++ b/azure/output.tf @@ -5,3 +5,4 @@ output "k8s_admin" { value = "${ k8s_admin}"} # fixme for use outside container output "ssh_key_setup" { value = "eval $(ssh-agent) ; ssh-add ${ var.data_dir }/.ssh/id_rsa"} output "ssh_via_bastion" { value = "ssh -At ${ var.admin_username }@${ module.bastion.bastion_fqdn } ssh ${ var.admin_username }@etcd1.${ var.internal_tld }"} +output "kubeconfig" { value = "${ module.kubeconfig.kubeconfig }"} diff --git a/cross-cloud/cloud.tf b/cross-cloud/cloud.tf index 011bb62..a000e86 100644 --- a/cross-cloud/cloud.tf +++ b/cross-cloud/cloud.tf @@ -19,32 +19,11 @@ module "packet" { packet_project_id = "${ var.packet_project_id }" } -module "aws-kubeconfig" { - source = "../kubeconfig" - admin_key_pem = "${ var.data_dir }/aws/.cfssl/k8s-admin-key.pem" - admin_pem = "${ var.data_dir }/aws/.cfssl/k8s-admin.pem" - ca_pem = "${ var.data_dir }/aws/.cfssl/ca.pem" - data_dir = "${ var.data_dir }" - fqdn_k8s = "${ module.aws.external_elb }" - name = "${ var.name }" +data "template_file" "kubeconfig" { + template = < Date: Fri, 21 Apr 2017 10:11:48 +1200 Subject: [PATCH 109/149] Add Internal LB Domain --- gce/cert.tf | 1 - gce/gce.tf | 41 ----------------------------- gce/init-cfssl | 11 +++----- gce/input.tf | 1 - gce/modules.tf | 6 ++--- gce/modules/dns/dns.tf | 8 ++++++ gce/modules/dns/input.tf | 1 + gce/modules/etcd/input.tf | 1 - gce/modules/etcd/internal_lb.tf | 5 ++-- gce/modules/etcd/nodes.tf | 7 +++++ gce/modules/etcd/output.tf | 8 ++---- gce/modules/security/security.tf | 2 +- gce/modules/worker/cloud-config.tf | 3 ++- gce/modules/worker/cloud-config.yml | 4 +-- gce/modules/worker/input.tf | 2 +- gce/output.tf | 2 +- 16 files changed, 34 insertions(+), 69 deletions(-) diff --git a/gce/cert.tf b/gce/cert.tf index ea5c207..2731331 100644 --- a/gce/cert.tf +++ b/gce/cert.tf @@ -8,7 +8,6 @@ ${ var.data_dir }/.cfssl \ ${ var.region } \ ${ var.internal-tld } \ ${ var.k8s-service-ip } \ -${ var.internal_lb } \ ${ var.project } \ ${ var.domain } \ ${ var.name } diff --git a/gce/gce.tf b/gce/gce.tf index eff33d1..0af3f1c 100644 --- a/gce/gce.tf +++ b/gce/gce.tf @@ -8,45 +8,4 @@ provider "google" { provider "dnsimple" { } -# resource "google_project" "company-env" { -# project_id = "${var.ENVIRONMENT}" -# org_id = "${var.GCP_ORG_ID}" -# name = "${var.ENVIRONMENT}" -# skip_delete = "true" -# } - -# // APIs to enable for above project -# resource "google_project_services" "company-env" { -# project = "${var.ENVIRONMENT}" -# services = ["compute_component", "container", "dns.googleapis.com", "sqladmin-json.googleapis.com", "monitoring.googleapis.com", "logging.googleapis.com", "sql-component-json.googleapis.com", "cloudmonitoring.googleapis.com", "storage-component-json.googleapis.com", "iam.googleapis.com"] -# } - -# resource "azurerm_resource_group" "cncf" { -# name = "${ var.name }" -# location = "${ var.location }" -# } - -# resource "azurerm_storage_account" "cncf" { -# # * azurerm_storage_account.cncf: name can only consist of lowercase letters -# # and numbers, and must be between 3 and 24 characters long FIXME: -# # storage_account name must be globally unique -# name = "${ var.name }cncfdemo" -# resource_group_name = "${ var.name }" -# location = "${ var.location }" -# account_type = "Standard_LRS" -# } - -# resource "azurerm_storage_container" "cncf" { -# name = "${ var.name }" -# resource_group_name = "${ var.name }" -# storage_account_name = "${ azurerm_storage_account.cncf.name }" -# container_access_type = "private" -# } - -# resource "azurerm_availability_set" "cncf" { -# name = "${ var.name }" -# resource_group_name = "${ var.name }" -# location = "${ var.location }" - -# } diff --git a/gce/init-cfssl b/gce/init-cfssl index e71bbf0..08cabb0 100755 --- a/gce/init-cfssl +++ b/gce/init-cfssl @@ -20,16 +20,13 @@ INTERNAL_TLD=$3 K8S_SERVICE_IP=$4 [ -z "$K8S_SERVICE_IP" ] && usage -INTERNAL_LB=$5 -[ -z "$INTERNAL_LB" ] && usage - -PROJECT=$6 +PROJECT=$5 [ -z "$PROJECT" ] && usage -DOMAIN=$7 +DOMAIN=$6 [ -z "$DOMAIN" ] && usage -NAME=$8 +NAME=$7 [ -z "$NAME" ] && usage @@ -114,7 +111,7 @@ _chmod ca # generate keys and certs generate k8s-admin client-server "${DEFAULT_HOSTS}" -generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},${INTERNAL_LB},endpoint.${NAME}.${DOMAIN}" +generate k8s-apiserver client-server "${DEFAULT_HOSTS},${K8S_SERVICE_IP},master.${NAME}.${DOMAIN},endpoint.${NAME}.${DOMAIN}" generate k8s-etcd client-server "*.c.${PROJECT}.internal,test-master1.c.${PROJECT}.internal,test-master2.c.${PROJECT}.internal,test-master3.c.${PROJECT}.internal" generate k8s-worker client "${DEFAULT_HOSTS}" diff --git a/gce/input.tf b/gce/input.tf index ee620c6..2f3bfb1 100644 --- a/gce/input.tf +++ b/gce/input.tf @@ -25,5 +25,4 @@ variable "dns-service-ip" { default = "10.3.0.10" } variable "allow-ssh-cidr" { default = "0.0.0.0/0" } variable "data_dir" { default = "/cncf/data" } variable "name-servers-file" { default = "google_dns_zone"} -variable "internal_lb" { default = "10.0.0.100" } variable "domain" { default = "cncf.ci" } diff --git a/gce/modules.tf b/gce/modules.tf index 527f398..1a4dd9d 100644 --- a/gce/modules.tf +++ b/gce/modules.tf @@ -17,7 +17,8 @@ module "vpc" { module "dns" { source = "./modules/dns" name = "${ var.name }" - external_lb = "${ module.etcd.external-lb }" + external_lb = "${ module.etcd.external_lb }" + internal_lb = "${ module.etcd.internal_lb}" master_node_count = "${ var.master_node_count }" domain = "${ var.domain }" } @@ -30,7 +31,6 @@ module "dns" { project = "${ var.project }" network = "${ module.vpc.network }" subnetwork = "${ module.vpc.subnetwork }" - internal_lb = "${ var.internal_lb }" name-servers-file = "${ var.name-servers-file }" # admin-username = "${ var.admin-username }" master_node_count = "${ var.master_node_count }" @@ -88,7 +88,6 @@ module "worker" { region = "${ var.region }" zone = "${ var.zone }" project = "${ var.project }" - internal_lb = "${ var.internal_lb }" # admin-username = "${ var.admin-username }" worker-node-count = "${ var.worker-node-count }" # worker-vm-size = "${ var.worker-vm-size }" @@ -107,6 +106,7 @@ module "worker" { kubelet-image-tag = "${ var.kubelet-image-tag }" dns-service-ip = "${ var.dns-service-ip }" internal-tld = "${ var.internal-tld }" + domain = "${ var.domain }" ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" k8s-worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" k8s-worker-key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" diff --git a/gce/modules/dns/dns.tf b/gce/modules/dns/dns.tf index 8508e3f..52aca36 100644 --- a/gce/modules/dns/dns.tf +++ b/gce/modules/dns/dns.tf @@ -12,3 +12,11 @@ resource "dnsimple_record" "A-public-endpoint" { ttl = "${ var.record_ttl }" domain = "${ var.domain }" } + +resource "dnsimple_record" "A-internal-lb" { + name = "master.${ var.name }" + value = "${ var.internal_lb}" + type = "A" + ttl = "${ var.record_ttl }" + domain = "${ var.domain }" +} diff --git a/gce/modules/dns/input.tf b/gce/modules/dns/input.tf index c5bc7f5..bc49d50 100644 --- a/gce/modules/dns/input.tf +++ b/gce/modules/dns/input.tf @@ -1,5 +1,6 @@ variable "name" {} variable "master_node_count" {} variable "external_lb" {} +variable "internal_lb" {} variable "domain" {} variable "record_ttl" { default = "60" } diff --git a/gce/modules/etcd/input.tf b/gce/modules/etcd/input.tf index 3ee6405..cd0fab7 100644 --- a/gce/modules/etcd/input.tf +++ b/gce/modules/etcd/input.tf @@ -1,7 +1,6 @@ # variable "location" {} # variable "subnet-id" {} variable "name" {} -variable "internal_lb" {} variable "region" {} variable "zone" {} variable "project" {} diff --git a/gce/modules/etcd/internal_lb.tf b/gce/modules/etcd/internal_lb.tf index 7c756a7..d28d5ae 100644 --- a/gce/modules/etcd/internal_lb.tf +++ b/gce/modules/etcd/internal_lb.tf @@ -1,9 +1,8 @@ -resource "google_compute_forwarding_rule" "default" { +resource "google_compute_forwarding_rule" "cncf" { name = "${ var.name }" load_balancing_scheme = "INTERNAL" - ip_address = "${ var.internal_lb }" region = "${ var.region }" - ports = ["8080"] + ports = ["8080", "443"] network = "${ var.network }" subnetwork = "${ var.subnetwork }" backend_service = "${ google_compute_region_backend_service.cncf.self_link }" diff --git a/gce/modules/etcd/nodes.tf b/gce/modules/etcd/nodes.tf index a27be48..ba01b56 100644 --- a/gce/modules/etcd/nodes.tf +++ b/gce/modules/etcd/nodes.tf @@ -25,10 +25,17 @@ resource "google_compute_target_pool" "cncf" { resource "google_compute_instance_group" "cncf" { name = "${ var.name }" instances = ["${google_compute_instance.cncf.*.self_link}"] + named_port = { name = "http" port = "8080" } + + named_port { + name = "https" + port = "443" + } + zone = "${ var.zone }" } diff --git a/gce/modules/etcd/output.tf b/gce/modules/etcd/output.tf index f866df5..68a7092 100644 --- a/gce/modules/etcd/output.tf +++ b/gce/modules/etcd/output.tf @@ -1,8 +1,4 @@ -output "external-lb" { value = "${google_compute_forwarding_rule.external.ip_address }" } -# output "fqdn-lb" { value = "${azurerm_public_ip.cncf.fqdn}" } +output "external_lb" { value = "${ google_compute_forwarding_rule.external.ip_address }" } +output "internal_lb" { value = "${ google_compute_forwarding_rule.cncf.ip_address }" } -# # output "depends-id" { value = "${ null_resource.dummy_dependency.id }" } -# # output "external-elb" { value = "${ aws_elb.external.dns_name }" } -# # output "internal-ips" { value = "${ join(",", aws_instance.etcd.*.public_ip) }" } -# output "master-ips" { value = ["${ google_compute_instance.cncf.*.network_interface.0.address }"] } diff --git a/gce/modules/security/security.tf b/gce/modules/security/security.tf index 2ab6609..9d93110 100644 --- a/gce/modules/security/security.tf +++ b/gce/modules/security/security.tf @@ -17,7 +17,7 @@ resource "google_compute_firewall" "allow-health-check" { allow { protocol = "tcp" - ports = ["8080"] + ports = ["8080", "443"] } source_ranges = ["130.211.0.0/22","35.191.0.0/16","10.0.0.0/16"] diff --git a/gce/modules/worker/cloud-config.tf b/gce/modules/worker/cloud-config.tf index ed01ffd..d7abdd5 100644 --- a/gce/modules/worker/cloud-config.tf +++ b/gce/modules/worker/cloud-config.tf @@ -10,7 +10,8 @@ data "template_file" "cloud-config" { ca = "${ base64encode(var.ca) }" k8s-worker = "${ base64encode(var.k8s-worker) }" k8s-worker-key = "${ base64encode(var.k8s-worker-key) }" - internal_lb = "${ var.internal_lb }" + name = "${ var.name }" + domain = "${ var.domain }" # cloud-config = "${ base64encode(var.cloud-config) }" } } diff --git a/gce/modules/worker/cloud-config.yml b/gce/modules/worker/cloud-config.yml index 5e1c82b..3906407 100644 --- a/gce/modules/worker/cloud-config.yml +++ b/gce/modules/worker/cloud-config.yml @@ -43,7 +43,7 @@ coreos: ExecStartPre=/usr/bin/mount --make-shared /var/lib/kubelet ExecStart=/usr/lib/coreos/kubelet-wrapper \ --allow-privileged=true \ - --api-servers=http://${ internal_lb }:8080 \ + --api-servers=http://master.${ name }.${ domain }:8080 \ --cloud-provider=gce \ --cluster-dns=${ dns-service-ip } \ --cluster-domain=${ cluster-domain } \ @@ -104,7 +104,7 @@ write-files: - /hyperkube - proxy - --kubeconfig=/etc/kubernetes/kubeconfig.yml - - --master=https://master.${ internal-tld } + - --master=https://master.${ name }.${ domain } - --proxy-mode=iptables securityContext: privileged: true diff --git a/gce/modules/worker/input.tf b/gce/modules/worker/input.tf index 122d605..e43082b 100644 --- a/gce/modules/worker/input.tf +++ b/gce/modules/worker/input.tf @@ -5,7 +5,6 @@ variable "project" {} variable "name" {} # variable "worker-vm-size" {} variable "worker-node-count" {} -variable "internal_lb" {} # variable "image-publisher" {} # variable "image-offer" {} # variable "image-sku" {} @@ -24,4 +23,5 @@ variable "kubelet-image-tag" {} variable "ca" {} variable "k8s-worker" {} variable "k8s-worker-key" {} +variable "domain" {} # variable "cloud-config" {} diff --git a/gce/output.tf b/gce/output.tf index 425cb75..1c62258 100644 --- a/gce/output.tf +++ b/gce/output.tf @@ -12,7 +12,7 @@ #output "cluster-domain" { value = "${ var.cluster-domain }" } #output "dns-service-ip" { value = "${ var.dns-service-ip }" } #output "etcd1-ip" { value = "${ element( split(",", var.etcd-ips), 0 ) }" } -output "external_lb" { value = "${ module.etcd.external-lb }" } +output "external_lb" { value = "${ module.etcd.external_lb }" } #output "internal-tld" { value = "${ var.internal-tld }" } #output "name" { value = "${ var.name }" } #output "region" { value = "${ var.aws["region"] }" } From dfe589b335be530980a6c84df839519bf0710464 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Fri, 21 Apr 2017 10:15:49 +1200 Subject: [PATCH 110/149] ensure gce uses shared kubeconfig --- gce/modules.tf | 13 ++++++------- gce/modules/kubeconfig/io.tf | 8 -------- gce/modules/kubeconfig/kubeconfig.tf | 19 ------------------- 3 files changed, 6 insertions(+), 34 deletions(-) delete mode 100644 gce/modules/kubeconfig/io.tf delete mode 100644 gce/modules/kubeconfig/kubeconfig.tf diff --git a/gce/modules.tf b/gce/modules.tf index 1a4dd9d..17927aa 100644 --- a/gce/modules.tf +++ b/gce/modules.tf @@ -116,17 +116,16 @@ module "worker" { module "kubeconfig" { - source = "./modules/kubeconfig" + source = "../kubeconfig" - admin-key-pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" - admin-pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" - ca-pem = "${ var.data_dir }/.cfssl/ca.pem" - external_fqdn = "endpoint.${ var.name }.${ var.domain }" + admin_key_pem = "${ var.data_dir }/.cfssl/k8s-admin-key.pem" + admin_pem = "${ var.data_dir }/.cfssl/k8s-admin.pem" + ca_pem = "${ var.data_dir }/.cfssl/ca.pem" + data_dir = "${ var.data_dir }" + fqdn_k8s = "endpoint.${ var.name }.${ var.domain }" name = "${ var.name }" } - - module "security" { source = "./modules/security" diff --git a/gce/modules/kubeconfig/io.tf b/gce/modules/kubeconfig/io.tf deleted file mode 100644 index 538875a..0000000 --- a/gce/modules/kubeconfig/io.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "admin-key-pem" {} -variable "admin-pem" {} -variable "ca-pem" {} -variable "external_fqdn" {} -variable "name" {} - - -# output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/gce/modules/kubeconfig/kubeconfig.tf b/gce/modules/kubeconfig/kubeconfig.tf deleted file mode 100644 index 8052878..0000000 --- a/gce/modules/kubeconfig/kubeconfig.tf +++ /dev/null @@ -1,19 +0,0 @@ -resource "null_resource" "kubeconfig" { - - provisioner "local-exec" { - command = < Date: Mon, 24 Apr 2017 06:06:09 +1200 Subject: [PATCH 111/149] move terraform code into provisioning --- gce/input.tf | 28 ------------ kubeconfig/kubeconfig.tf | 26 ----------- Dockerfile => provisioning/Dockerfile | 0 {aws => provisioning/aws}/Readme.mkd | 0 {aws => provisioning/aws}/aws.tf | 0 {aws => provisioning/aws}/cert.tf | 0 {aws => provisioning/aws}/cleanup.tf | 0 {aws => provisioning/aws}/init-cfssl | 0 {aws => provisioning/aws}/input.tf | 0 {aws => provisioning/aws}/keypair.tf | 0 {aws => provisioning/aws}/modules.tf | 0 .../aws}/modules/bastion/ec2.tf | 0 .../aws}/modules/bastion/input.tf | 0 .../aws}/modules/bastion/output.tf | 0 .../aws}/modules/bastion/user-data.yml | 0 {aws => provisioning/aws}/modules/dns/dns.tf | 0 .../aws}/modules/dns/input.tf | 0 .../aws}/modules/dns/output.tf | 0 .../aws}/modules/etcd/cloud-config.tf | 0 .../aws}/modules/etcd/cloud-config.yml | 0 {aws => provisioning/aws}/modules/etcd/ec2.tf | 0 {aws => provisioning/aws}/modules/etcd/elb.tf | 0 .../aws}/modules/etcd/input.tf | 0 .../aws}/modules/etcd/kube-apiserver.yml | 0 .../aws}/modules/etcd/output.tf | 0 {aws => provisioning/aws}/modules/iam/etcd.tf | 0 {aws => provisioning/aws}/modules/iam/io.tf | 0 .../aws}/modules/iam/worker.tf | 0 .../aws}/modules/security/io.tf | 0 .../aws}/modules/security/security.tf | 0 .../aws}/modules/vpc/input.tf | 0 .../aws}/modules/vpc/output.tf | 0 .../aws}/modules/vpc/private.tf | 0 .../aws}/modules/vpc/public.tf | 0 {aws => provisioning/aws}/modules/vpc/vpc.tf | 0 .../aws}/modules/worker/cloud-config.tf | 0 .../aws}/modules/worker/cloud-config.yml | 0 .../aws}/modules/worker/ec2.tf | 0 .../aws}/modules/worker/input.tf | 0 .../aws}/modules/worker/output.tf | 0 {aws => provisioning/aws}/output.tf | 0 {aws => provisioning/aws}/wait-for-cluster | 0 {azure => provisioning/azure}/azure.tf | 0 .../azure}/docs/azure_app_endpoints.png | Bin .../azure}/docs/azure_app_registration.png | Bin .../azure}/docs/guid_from_oauth_endpoint.png | Bin .../azure}/docs/key_generation_copy_me.png | Bin .../azure}/docs/research.md | 0 .../azure}/docs/research.org | 0 .../azure}/docs/web_api_application_type.png | Bin {azure => provisioning/azure}/init-cfssl | 0 {azure => provisioning/azure}/input.tf | 0 {azure => provisioning/azure}/modules.tf | 0 .../azure}/modules/bastion/bastion-node.tf | 0 .../modules/bastion/bastion-user-data.yml | 0 .../azure}/modules/bastion/input.tf | 0 .../azure}/modules/bastion/output.tf | 0 .../azure}/modules/dns/dns.tf | 0 .../azure}/modules/dns/input.tf | 0 .../azure}/modules/dns/output.tf | 0 .../azure}/modules/etcd/etcd-cloud-config.tf | 0 .../azure}/modules/etcd/etcd-cloud-config.yml | 0 .../azure}/modules/etcd/etcd-load-balancer.tf | 0 .../azure}/modules/etcd/etcd-nodes.tf | 0 .../azure}/modules/etcd/input.tf | 0 .../azure}/modules/etcd/kube-apiserver.yml | 0 .../azure}/modules/etcd/output.tf | 0 .../azure}/modules/network/input.tf | 0 .../azure}/modules/network/output.tf | 0 .../azure}/modules/network/virtual_network.tf | 0 .../azure}/modules/worker/input.tf | 0 .../modules/worker/worker-cloud-config.tf | 0 .../modules/worker/worker-cloud-config.yml | 0 .../azure}/modules/worker/worker-nodes.tf | 0 {azure => provisioning/azure}/output.tf | 0 {azure => provisioning/azure}/readme.org | 0 {azure => provisioning/azure}/runme | 0 .../azure/servicePrincipalProfile.json | 0 .../azure}/ssl-ssh-cloud.tf | 0 .../azure}/wait-for-cluster | 0 .../cross-cloud}/cloud.tf | 23 +++++++++- .../cross-cloud}/input.tf | 0 .../cross-cloud}/output.tf | 0 entrypoint.sh => provisioning/entrypoint.sh | 13 +++--- {gce => provisioning/gce}/cert.tf | 4 +- {gce => provisioning/gce}/cloud-config.tf | 0 {gce => provisioning/gce}/gce.tf | 0 {gce => provisioning/gce}/init-cfssl | 0 provisioning/gce/input.tf | 28 ++++++++++++ {gce => provisioning/gce}/keypair.tf | 0 {gce => provisioning/gce}/modules.tf | 40 ++++++++--------- .../gce}/modules/bastion/input.tf | 4 +- .../gce}/modules/bastion/node.tf | 8 ++-- .../gce}/modules/bastion/output.tf | 0 .../gce}/modules/bastion/user-data.yml | 2 +- {gce => provisioning/gce}/modules/dns/dns.tf | 0 .../gce}/modules/dns/input.tf | 0 .../gce}/modules/dns/output.tf | 0 .../gce}/modules/etcd/cloud-config.tf | 16 +++---- .../gce}/modules/etcd/cloud-config.yml | 18 ++++---- .../gce}/modules/etcd/discovery.tf | 0 .../gce}/modules/etcd/external_lb.tf | 0 .../gce}/modules/etcd/input.tf | 20 ++++----- .../gce}/modules/etcd/internal_lb.tf | 0 .../gce}/modules/etcd/load-balancer.tf | 0 .../gce}/modules/etcd/nodes.tf | 0 .../gce}/modules/etcd/output.tf | 0 .../gce}/modules/security/io.tf | 0 .../gce}/modules/security/security.tf | 0 .../gce}/modules/vpc/azure-security.tf | 0 .../gce}/modules/vpc/gce-subnet.tf | 0 {gce => provisioning/gce}/modules/vpc/io.tf | 0 .../gce}/modules/vpc/output.tf | 0 .../gce}/modules/vpc/private.tf | 0 .../gce}/modules/vpc/public.tf | 0 {gce => provisioning/gce}/modules/vpc/vpc.tf | 0 .../gce}/modules/worker/cloud-config.tf | 10 ++--- .../gce}/modules/worker/cloud-config.yml | 12 ++--- .../gce}/modules/worker/input.tf | 14 +++--- .../gce}/modules/worker/nodes.tf | 10 ++--- {gce => provisioning/gce}/output.tf | 8 ++-- {gce => provisioning/gce}/runme | 0 {gce => provisioning/gce}/wait-for-cluster | 0 .../kubeconfig}/input.tf | 0 provisioning/kubeconfig/kubeconfig.tf | 41 ++++++++++++++++++ .../kubeconfig}/output.tf | 0 {packet => provisioning/packet}/init-cfssl | 0 {packet => provisioning/packet}/input.tf | 0 {packet => provisioning/packet}/modules.tf | 0 .../packet}/modules/dns/dns.tf | 0 .../packet}/modules/dns/input.tf | 0 provisioning/packet/modules/dns/output.tf | 0 .../packet}/modules/etcd/discovery.tf | 0 .../packet}/modules/etcd/etcd-cloud-config.tf | 0 .../modules/etcd/etcd-cloud-config.yml | 0 .../packet}/modules/etcd/etcd-nodes.tf | 0 .../packet}/modules/etcd/input.tf | 0 .../packet}/modules/etcd/kube-apiserver.yml | 0 .../packet}/modules/etcd/output.tf | 0 .../packet}/modules/worker/input.tf | 0 .../packet}/modules/worker/output.tf | 0 .../modules/worker/worker-cloud-config.tf | 0 .../modules/worker/worker-cloud-config.yml | 0 .../packet}/modules/worker/worker-nodes.tf | 0 {packet => provisioning/packet}/output.tf | 0 {packet => provisioning/packet}/packet.tf | 0 .../packet}/ssl-ssh-cloud.tf | 0 147 files changed, 179 insertions(+), 146 deletions(-) delete mode 100644 gce/input.tf delete mode 100644 kubeconfig/kubeconfig.tf rename Dockerfile => provisioning/Dockerfile (100%) rename {aws => provisioning/aws}/Readme.mkd (100%) rename {aws => provisioning/aws}/aws.tf (100%) rename {aws => provisioning/aws}/cert.tf (100%) rename {aws => provisioning/aws}/cleanup.tf (100%) rename {aws => provisioning/aws}/init-cfssl (100%) rename {aws => provisioning/aws}/input.tf (100%) rename {aws => provisioning/aws}/keypair.tf (100%) rename {aws => provisioning/aws}/modules.tf (100%) rename {aws => provisioning/aws}/modules/bastion/ec2.tf (100%) rename {aws => provisioning/aws}/modules/bastion/input.tf (100%) rename {aws => provisioning/aws}/modules/bastion/output.tf (100%) rename {aws => provisioning/aws}/modules/bastion/user-data.yml (100%) rename {aws => provisioning/aws}/modules/dns/dns.tf (100%) rename {aws => provisioning/aws}/modules/dns/input.tf (100%) rename {aws => provisioning/aws}/modules/dns/output.tf (100%) rename {aws => provisioning/aws}/modules/etcd/cloud-config.tf (100%) rename {aws => provisioning/aws}/modules/etcd/cloud-config.yml (100%) rename {aws => provisioning/aws}/modules/etcd/ec2.tf (100%) rename {aws => provisioning/aws}/modules/etcd/elb.tf (100%) rename {aws => provisioning/aws}/modules/etcd/input.tf (100%) rename {aws => provisioning/aws}/modules/etcd/kube-apiserver.yml (100%) rename {aws => provisioning/aws}/modules/etcd/output.tf (100%) rename {aws => provisioning/aws}/modules/iam/etcd.tf (100%) rename {aws => provisioning/aws}/modules/iam/io.tf (100%) rename {aws => provisioning/aws}/modules/iam/worker.tf (100%) rename {aws => provisioning/aws}/modules/security/io.tf (100%) rename {aws => provisioning/aws}/modules/security/security.tf (100%) rename {aws => provisioning/aws}/modules/vpc/input.tf (100%) rename {aws => provisioning/aws}/modules/vpc/output.tf (100%) rename {aws => provisioning/aws}/modules/vpc/private.tf (100%) rename {aws => provisioning/aws}/modules/vpc/public.tf (100%) rename {aws => provisioning/aws}/modules/vpc/vpc.tf (100%) rename {aws => provisioning/aws}/modules/worker/cloud-config.tf (100%) rename {aws => provisioning/aws}/modules/worker/cloud-config.yml (100%) rename {aws => provisioning/aws}/modules/worker/ec2.tf (100%) rename {aws => provisioning/aws}/modules/worker/input.tf (100%) rename {aws => provisioning/aws}/modules/worker/output.tf (100%) rename {aws => provisioning/aws}/output.tf (100%) rename {aws => provisioning/aws}/wait-for-cluster (100%) rename {azure => provisioning/azure}/azure.tf (100%) rename {azure => provisioning/azure}/docs/azure_app_endpoints.png (100%) rename {azure => provisioning/azure}/docs/azure_app_registration.png (100%) rename {azure => provisioning/azure}/docs/guid_from_oauth_endpoint.png (100%) rename {azure => provisioning/azure}/docs/key_generation_copy_me.png (100%) rename {azure => provisioning/azure}/docs/research.md (100%) rename {azure => provisioning/azure}/docs/research.org (100%) rename {azure => provisioning/azure}/docs/web_api_application_type.png (100%) rename {azure => provisioning/azure}/init-cfssl (100%) rename {azure => provisioning/azure}/input.tf (100%) rename {azure => provisioning/azure}/modules.tf (100%) rename {azure => provisioning/azure}/modules/bastion/bastion-node.tf (100%) rename {azure => provisioning/azure}/modules/bastion/bastion-user-data.yml (100%) rename {azure => provisioning/azure}/modules/bastion/input.tf (100%) rename {azure => provisioning/azure}/modules/bastion/output.tf (100%) rename {azure => provisioning/azure}/modules/dns/dns.tf (100%) rename {azure => provisioning/azure}/modules/dns/input.tf (100%) rename {azure => provisioning/azure}/modules/dns/output.tf (100%) rename {azure => provisioning/azure}/modules/etcd/etcd-cloud-config.tf (100%) rename {azure => provisioning/azure}/modules/etcd/etcd-cloud-config.yml (100%) rename {azure => provisioning/azure}/modules/etcd/etcd-load-balancer.tf (100%) rename {azure => provisioning/azure}/modules/etcd/etcd-nodes.tf (100%) rename {azure => provisioning/azure}/modules/etcd/input.tf (100%) rename {azure => provisioning/azure}/modules/etcd/kube-apiserver.yml (100%) rename {azure => provisioning/azure}/modules/etcd/output.tf (100%) rename {azure => provisioning/azure}/modules/network/input.tf (100%) rename {azure => provisioning/azure}/modules/network/output.tf (100%) rename {azure => provisioning/azure}/modules/network/virtual_network.tf (100%) rename {azure => provisioning/azure}/modules/worker/input.tf (100%) rename {azure => provisioning/azure}/modules/worker/worker-cloud-config.tf (100%) rename {azure => provisioning/azure}/modules/worker/worker-cloud-config.yml (100%) rename {azure => provisioning/azure}/modules/worker/worker-nodes.tf (100%) rename {azure => provisioning/azure}/output.tf (100%) rename {azure => provisioning/azure}/readme.org (100%) rename {azure => provisioning/azure}/runme (100%) rename gce/modules/dns/output.tf => provisioning/azure/servicePrincipalProfile.json (100%) rename {azure => provisioning/azure}/ssl-ssh-cloud.tf (100%) rename {azure => provisioning/azure}/wait-for-cluster (100%) rename {cross-cloud => provisioning/cross-cloud}/cloud.tf (62%) rename {cross-cloud => provisioning/cross-cloud}/input.tf (100%) rename {cross-cloud => provisioning/cross-cloud}/output.tf (100%) rename entrypoint.sh => provisioning/entrypoint.sh (86%) rename {gce => provisioning/gce}/cert.tf (90%) rename {gce => provisioning/gce}/cloud-config.tf (100%) rename {gce => provisioning/gce}/gce.tf (100%) rename {gce => provisioning/gce}/init-cfssl (100%) create mode 100644 provisioning/gce/input.tf rename {gce => provisioning/gce}/keypair.tf (100%) rename {gce => provisioning/gce}/modules.tf (82%) rename {gce => provisioning/gce}/modules/bastion/input.tf (89%) rename {gce => provisioning/gce}/modules/bastion/node.tf (93%) rename {gce => provisioning/gce}/modules/bastion/output.tf (100%) rename {gce => provisioning/gce}/modules/bastion/user-data.yml (80%) rename {gce => provisioning/gce}/modules/dns/dns.tf (100%) rename {gce => provisioning/gce}/modules/dns/input.tf (100%) rename {packet => provisioning/gce}/modules/dns/output.tf (100%) rename {gce => provisioning/gce}/modules/etcd/cloud-config.tf (67%) rename {gce => provisioning/gce}/modules/etcd/cloud-config.yml (93%) rename {gce => provisioning/gce}/modules/etcd/discovery.tf (100%) rename {gce => provisioning/gce}/modules/etcd/external_lb.tf (100%) rename {gce => provisioning/gce}/modules/etcd/input.tf (71%) rename {gce => provisioning/gce}/modules/etcd/internal_lb.tf (100%) rename {gce => provisioning/gce}/modules/etcd/load-balancer.tf (100%) rename {gce => provisioning/gce}/modules/etcd/nodes.tf (100%) rename {gce => provisioning/gce}/modules/etcd/output.tf (100%) rename {gce => provisioning/gce}/modules/security/io.tf (100%) rename {gce => provisioning/gce}/modules/security/security.tf (100%) rename {gce => provisioning/gce}/modules/vpc/azure-security.tf (100%) rename {gce => provisioning/gce}/modules/vpc/gce-subnet.tf (100%) rename {gce => provisioning/gce}/modules/vpc/io.tf (100%) rename {gce => provisioning/gce}/modules/vpc/output.tf (100%) rename {gce => provisioning/gce}/modules/vpc/private.tf (100%) rename {gce => provisioning/gce}/modules/vpc/public.tf (100%) rename {gce => provisioning/gce}/modules/vpc/vpc.tf (100%) rename {gce => provisioning/gce}/modules/worker/cloud-config.tf (61%) rename {gce => provisioning/gce}/modules/worker/cloud-config.yml (93%) rename {gce => provisioning/gce}/modules/worker/input.tf (72%) rename {gce => provisioning/gce}/modules/worker/nodes.tf (90%) rename {gce => provisioning/gce}/output.tf (79%) rename {gce => provisioning/gce}/runme (100%) rename {gce => provisioning/gce}/wait-for-cluster (100%) rename {kubeconfig => provisioning/kubeconfig}/input.tf (100%) create mode 100644 provisioning/kubeconfig/kubeconfig.tf rename {kubeconfig => provisioning/kubeconfig}/output.tf (100%) rename {packet => provisioning/packet}/init-cfssl (100%) rename {packet => provisioning/packet}/input.tf (100%) rename {packet => provisioning/packet}/modules.tf (100%) rename {packet => provisioning/packet}/modules/dns/dns.tf (100%) rename {packet => provisioning/packet}/modules/dns/input.tf (100%) create mode 100644 provisioning/packet/modules/dns/output.tf rename {packet => provisioning/packet}/modules/etcd/discovery.tf (100%) rename {packet => provisioning/packet}/modules/etcd/etcd-cloud-config.tf (100%) rename {packet => provisioning/packet}/modules/etcd/etcd-cloud-config.yml (100%) rename {packet => provisioning/packet}/modules/etcd/etcd-nodes.tf (100%) rename {packet => provisioning/packet}/modules/etcd/input.tf (100%) rename {packet => provisioning/packet}/modules/etcd/kube-apiserver.yml (100%) rename {packet => provisioning/packet}/modules/etcd/output.tf (100%) rename {packet => provisioning/packet}/modules/worker/input.tf (100%) rename {packet => provisioning/packet}/modules/worker/output.tf (100%) rename {packet => provisioning/packet}/modules/worker/worker-cloud-config.tf (100%) rename {packet => provisioning/packet}/modules/worker/worker-cloud-config.yml (100%) rename {packet => provisioning/packet}/modules/worker/worker-nodes.tf (100%) rename {packet => provisioning/packet}/output.tf (100%) rename {packet => provisioning/packet}/packet.tf (100%) rename {packet => provisioning/packet}/ssl-ssh-cloud.tf (100%) diff --git a/gce/input.tf b/gce/input.tf deleted file mode 100644 index 2f3bfb1..0000000 --- a/gce/input.tf +++ /dev/null @@ -1,28 +0,0 @@ -variable "name" { default = "test" } -variable "internal-tld" { default = "cncf.demo" } -variable "master_node_count" { default = "3" } -variable "worker-node-count" { default = "3" } -variable "master-vm-size" { default = "Standard_A2" } -variable "worker-vm-size" { default = "Standard_A2" } -variable "bastion-vm-size" { default = "Standard_A2" } -# Set from https://quay.io/repository/coreos/hyperkube?tab=tags -variable "kubelet-image-url" { default = "quay.io/coreos/hyperkube"} -variable "kubelet-image-tag" { default = "v1.4.7_coreos.0"} -variable "image-publisher" { default = "CoreOS" } -variable "image-offer" { default = "CoreOS" } -variable "image-sku" { default = "Stable" } -variable "image-version" { default = "1298.6.0" } -variable "region" { default = "us-central1" } -variable "zone" { default = "us-central1-a" } -variable "project" { default = "test-163823" } -variable "cluster-domain" { default = "cluster.local" } -variable "admin-username" { default = "cncf"} -variable "cidr" { default = "10.0.0.0/16" } -variable "pod-cidr" { default = "10.2.0.0/16" } -variable "service-cidr" { default = "10.3.0.0/24" } -variable "k8s-service-ip" { default = "10.3.0.1" } -variable "dns-service-ip" { default = "10.3.0.10" } -variable "allow-ssh-cidr" { default = "0.0.0.0/0" } -variable "data_dir" { default = "/cncf/data" } -variable "name-servers-file" { default = "google_dns_zone"} -variable "domain" { default = "cncf.ci" } diff --git a/kubeconfig/kubeconfig.tf b/kubeconfig/kubeconfig.tf deleted file mode 100644 index a3cd443..0000000 --- a/kubeconfig/kubeconfig.tf +++ /dev/null @@ -1,26 +0,0 @@ -data "template_file" "kubeconfig" { - template = "echo kubectl config set-cluster cluster-${ var.name } --server=https://${ var.fqdn_k8s } --certificate-authority=${ var.ca_pem } && echo kubectl config set-credentials admin-${ var.name } --certificate-authority=${ var.ca_pem } --client-key=${ var.admin_key_pem } --client-certificate=${ var.admin_pem } && echo kubectl config set-context ${ var.name } --cluster=cluster-${ var.name } --user=admin-${ var.name }" -} - - -# resource "null_resource" "kubeconfig" { - -# provisioner "local-exec" { -# command = < Date: Sun, 23 Apr 2017 19:47:13 +0000 Subject: [PATCH 112/149] Add gitlab-ci.yml to build our docker image --- .gitlab-ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..9da0928 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,14 @@ +# This file is a template, and might need editing before it works on your project. +# Official docker image. +#image: docker:latest + +#services: +# - docker:dind + +build: + stage: build + script: + - export IMAGE_TAG=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-') + - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY + - docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" ./provision + - docker push "$CI_REGISTRY_IMAGE:$IMAGE_TAG" From 9cc3f7e39b854fe25a693d993744cd4cfa1d8e55 Mon Sep 17 00:00:00 2001 From: Hippie Hacker Date: Sun, 23 Apr 2017 19:49:26 +0000 Subject: [PATCH 113/149] Update .gitlab-ci.yml to use dind --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9da0928..684c686 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,9 @@ # This file is a template, and might need editing before it works on your project. # Official docker image. -#image: docker:latest +image: docker:latest -#services: -# - docker:dind +services: + - docker:dind build: stage: build From d0b87149d8e8ec7f1f7e3e44110ac41ba577c1c1 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 07:54:16 +1200 Subject: [PATCH 114/149] Moving entrypoint.sh to provision.sh --- provisioning/Dockerfile | 8 +++----- provisioning/{entrypoint.sh => provision.sh} | 0 2 files changed, 3 insertions(+), 5 deletions(-) rename provisioning/{entrypoint.sh => provision.sh} (100%) diff --git a/provisioning/Dockerfile b/provisioning/Dockerfile index eccf7d4..3cf162a 100644 --- a/provisioning/Dockerfile +++ b/provisioning/Dockerfile @@ -46,14 +46,12 @@ RUN go get -u github.com/jakexks/terraform-provider-gzip && \ echo ' gzip = "terraform-provider-gzip"' >> ~/.terraformrc && \ echo } >> ~/.terraformrc - - #Add Terraform Modules COPY aws /aws/ COPY azure /azure/ -COPY entrypoint.sh /cncf/ -RUN chmod +x /cncf/entrypoint.sh -ENTRYPOINT ["/cncf/entrypoint.sh"] +COPY provision.sh /cncf/ +RUN chmod +x /cncf/provision.sh +ENTRYPOINT ["/cncf/provision.sh"] WORKDIR /cncf/data CMD ["aws-deploy"] diff --git a/provisioning/entrypoint.sh b/provisioning/provision.sh similarity index 100% rename from provisioning/entrypoint.sh rename to provisioning/provision.sh From f2554cc6f8f78cff1ff261cf6b6ed6b549a3064d Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 07:55:56 +1200 Subject: [PATCH 115/149] Checking for current dir of ci deploy --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 684c686..a32da37 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,6 +9,8 @@ build: stage: build script: - export IMAGE_TAG=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-') + - ls -la + - pwd - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY - docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" ./provision - docker push "$CI_REGISTRY_IMAGE:$IMAGE_TAG" From 419eb784aaff831f09fcb1a9c2f5719df714cfb8 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 07:57:19 +1200 Subject: [PATCH 116/149] Update path to ./provisioning --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a32da37..3425fa4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,8 +9,6 @@ build: stage: build script: - export IMAGE_TAG=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-') - - ls -la - - pwd - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY - - docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" ./provision + - docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" ./provisioning - docker push "$CI_REGISTRY_IMAGE:$IMAGE_TAG" From b71a4c58dbbc57edb1c0b21a9f6f4f8f5046e7ad Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 09:16:06 +1200 Subject: [PATCH 117/149] Tag for foo kubernetes runner --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3425fa4..61f9f99 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,8 @@ services: build: stage: build + tags: + - foo script: - export IMAGE_TAG=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-') - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY From 44019e8b30ff7282500572c45d063bf29910cc53 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 09:47:36 +1200 Subject: [PATCH 118/149] Set packet working hostnames to public dns --- provisioning/packet/modules/worker/worker-nodes.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/packet/modules/worker/worker-nodes.tf b/provisioning/packet/modules/worker/worker-nodes.tf index 5e18d7a..5be00b5 100644 --- a/provisioning/packet/modules/worker/worker-nodes.tf +++ b/provisioning/packet/modules/worker/worker-nodes.tf @@ -1,5 +1,5 @@ resource "packet_device" "workers" { - hostname = "worker-${ count.index + 1 }-${ var.name }" + hostname = "worker${ count.index + 1 }.${ var.internal_tld }" count = "${ var.worker_node_count }" facility = "${ var.packet_facility }" project_id = "${ var.packet_project_id }" From 0e364d4b9163a5121103e45e17eeead7e62d8650 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 11:18:00 +1200 Subject: [PATCH 119/149] Add flanneld for overlay --- .../packet/modules/etcd/etcd-cloud-config.yml | 25 ++++++++++++++++--- .../modules/worker/worker-cloud-config.yml | 18 +++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.yml b/provisioning/packet/modules/etcd/etcd-cloud-config.yml index 46fe1fb..85c11ec 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.yml @@ -22,13 +22,32 @@ coreos: - name: etcd2.service command: start - - name: docker.service + - name: flanneld.service command: start drop-ins: - - name: overlay.conf + - name: 50-network-config.conf content: | [Service] - Environment="DOCKER_OPTS=--storage-driver=overlay" + ExecStartPre=-/usr/bin/etcdctl mk /coreos.com/network/config \ + '{ "Network": "${ pod-cidr }", "Backend": { "Type": "vxlan" } }' + Restart=always + RestartSec=10 + + - name: docker.service + command: start + drop-ins: + - name: 40-flannel.conf + content: | + [Unit] + After=flanneld.service + Requires=flanneld.service + [Service] + Restart=always + RestartSec=10 + - name: overlay.conf + content: | + [Service] + Environment="DOCKER_OPTS=--storage-driver=overlay" - name: kubelet.service command: start diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index 6ae9411..39a9e94 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -14,8 +14,26 @@ coreos: - name: etcd2.service command: start + - name: flanneld.service + command: start + drop-ins: + - name: 50-network-config.conf + content: | + [Service] + Restart=always + RestartSec=10 + - name: docker.service command: start + drop-ins: + - name: 40-flannel.conf + content: | + [Unit] + After=flanneld.service + Requires=flanneld.service + [Service] + Restart=always + RestartSec=10 - name: kubelet.service command: start From ad0fb1c8e037805d2d3f7b6dfafefb60a96c63a5 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 11:18:55 +1200 Subject: [PATCH 120/149] Upgrade to Kubelet 1.6.1 --- provisioning/aws/input.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/aws/input.tf b/provisioning/aws/input.tf index 0983dd9..931bb95 100644 --- a/provisioning/aws/input.tf +++ b/provisioning/aws/input.tf @@ -32,5 +32,5 @@ variable "worker_node_max" { default = "5" } # Hyperkube # Set from https://quay.io/repository/coreos/hyperkube?tab=tags variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} -variable "kubelet_image_tag" { default = "v1.5.1_coreos.0"} +variable "kubelet_image_tag" { default = "v1.6.1_coreos.0"} From 0d54416c625d27580175eed6b9bfbd5690573fdc Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Mon, 24 Apr 2017 11:41:49 +1200 Subject: [PATCH 121/149] Use fqdn for hostnames on Packet --- provisioning/packet/modules/etcd/etcd-cloud-config.yml | 2 +- provisioning/packet/modules/etcd/etcd-nodes.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.yml b/provisioning/packet/modules/etcd/etcd-cloud-config.yml index 85c11ec..cee6a65 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.yml @@ -29,7 +29,7 @@ coreos: content: | [Service] ExecStartPre=-/usr/bin/etcdctl mk /coreos.com/network/config \ - '{ "Network": "${ pod-cidr }", "Backend": { "Type": "vxlan" } }' + '{ "Network": "${ pod_cidr }", "Backend": { "Type": "vxlan" } }' Restart=always RestartSec=10 diff --git a/provisioning/packet/modules/etcd/etcd-nodes.tf b/provisioning/packet/modules/etcd/etcd-nodes.tf index 9461ec1..8cf912d 100644 --- a/provisioning/packet/modules/etcd/etcd-nodes.tf +++ b/provisioning/packet/modules/etcd/etcd-nodes.tf @@ -1,5 +1,5 @@ resource "packet_device" "masters" { - hostname = "master-${ count.index + 1 }-${ var.name }" + hostname = "master${ count.index + 1 }.${ var.internal_tld }" count = "${ var.master_node_count }" facility = "${ var.packet_facility }" project_id = "${ var.packet_project_id }" From 648053f7eb1b12ae4f033c972c6aa9e0e6f8c57e Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 25 Apr 2017 04:50:37 +1200 Subject: [PATCH 122/149] Bringing up etcd on workers so flannel will function --- provisioning/packet/modules.tf | 1 + provisioning/packet/modules/worker/input.tf | 1 + provisioning/packet/modules/worker/worker-cloud-config.tf | 1 + provisioning/packet/modules/worker/worker-cloud-config.yml | 7 ++++--- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/provisioning/packet/modules.tf b/provisioning/packet/modules.tf index c2779a4..30d6824 100644 --- a/provisioning/packet/modules.tf +++ b/provisioning/packet/modules.tf @@ -79,6 +79,7 @@ module "worker" { ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" k8s_worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" k8s_worker_key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" + etcd_discovery = "${ var.data_dir }/etcd" data_dir = "${ var.data_dir }" } diff --git a/provisioning/packet/modules/worker/input.tf b/provisioning/packet/modules/worker/input.tf index f5e6f65..fbb88a9 100644 --- a/provisioning/packet/modules/worker/input.tf +++ b/provisioning/packet/modules/worker/input.tf @@ -16,3 +16,4 @@ variable "ca" {} variable "k8s_worker" {} variable "k8s_worker_key" {} variable "data_dir" {} +variable "etcd_discovery" {} diff --git a/provisioning/packet/modules/worker/worker-cloud-config.tf b/provisioning/packet/modules/worker/worker-cloud-config.tf index faaf91e..5edac7a 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.tf +++ b/provisioning/packet/modules/worker/worker-cloud-config.tf @@ -26,6 +26,7 @@ data "template_file" "worker_user_data" { ca = "${ gzip_me.ca.output }" k8s_worker = "${ gzip_me.k8s_worker.output }" k8s_worker_key = "${ gzip_me.k8s_worker_key.output }" + etcd_discovery = "${file(var.etcd_discovery)}" } } diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index 39a9e94..893ee8a 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -3,11 +3,12 @@ coreos: etcd2: - discovery-srv: ${ internal_tld } + debug: true + discovery: ${ etcd_discovery } peer-trusted-ca-file: /etc/kubernetes/ssl/ca.pem peer-client-cert-auth: true - peer-cert-file: /etc/kubernetes/ssl/k8s-worker.pem - peer-key-file: /etc/kubernetes/ssl/k8s-worker-key.pem + peer-cert-file: /etc/kubernetes/ssl/k8s-etcd.pem + peer-key-file: /etc/kubernetes/ssl/k8s-etcd-key.pem proxy: on units: From b0149e6a401375bd84a15b8c07040ab6298a9485 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 25 Apr 2017 04:51:27 +1200 Subject: [PATCH 123/149] Make provision.sh use relative paths --- provisioning/provision.sh | 63 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/provisioning/provision.sh b/provisioning/provision.sh index 9fadf7a..6c5e9a0 100755 --- a/provisioning/provision.sh +++ b/provisioning/provision.sh @@ -1,6 +1,5 @@ #!/bin/bash -# -# RUN ENTRYPOINT. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" set -e RED='\033[0;31m' @@ -8,69 +7,69 @@ NC='\033[0m' # No Color export TF_VAR_name="$2" export TF_VAR_internal_tld=${TF_VAR_name}.cncf.demo -export TF_VAR_data_dir=/cncf/data/${TF_VAR_name} +export TF_VAR_data_dir=${DIR}/data/${TF_VAR_name} # tfstate, sslcerts, and ssh keys are currently stored in TF_VAR_data_dir mkdir -p $TF_VAR_data_dir cd $TF_VAR_data_dir # Run CMD if [ "$1" = "aws-deploy" ] ; then - terraform get /cncf/aws && \ - terraform apply -target null_resource.ssl_gen /cncf/aws && \ + terraform get ${DIR}/aws && \ + terraform apply -target null_resource.ssl_gen ${DIR}/aws && \ time terraform apply /deploy/aws && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/ \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/kubeconfig \n\n'${NC} elif [ "$1" = "aws-destroy" ] ; then write_aws_config - time terraform destroy -force /cncf/aws + time terraform destroy -force ${DIR}/aws elif [ "$1" = "azure-deploy" ] ; then # There are some dependency issues around cert,sshkey,k8s_cloud_config, and dns # since they use files on disk that are created on the fly # should probably move these to data resources - terraform get /cncf/azure && \ - terraform apply -target null_resource.ssl_ssh_cloud_gen /cncf/cross-cloud && \ - terraform apply -target null_resource.dns_gen /cncf/azure && \ - time terraform apply /cncf/azure && \ + terraform get ${DIR}/azure && \ + terraform apply -target null_resource.ssl_ssh_cloud_gen ${DIR}/cross-cloud && \ + terraform apply -target null_resource.dns_gen ${DIR}/azure && \ + time terraform apply ${DIR}/azure && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "azure-destroy" ] ; then - time terraform destroy -force /cncf/azure + time terraform destroy -force ${DIR}/azure elif [ "$1" = "packet-deploy" ] ; then - terraform get /cncf/packet && \ - terraform apply -target module.etcd.null_resource.discovery_gen /cncf/packet && \ - terraform apply -target null_resource.ssl_ssh_gen /cncf/packet && \ - time terraform apply /cncf/packet && \ + terraform get ${DIR}/packet && \ + terraform apply -target module.etcd.null_resource.discovery_gen ${DIR}/packet && \ + terraform apply -target null_resource.ssl_ssh_gen ${DIR}/packet && \ + time terraform apply ${DIR}/packet && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "packet-destroy" ] ; then - time terraform destroy -force /cncf/packet + time terraform destroy -force ${DIR}/packet elif [ "$1" = "gce-deploy" ] ; then - terraform get /cncf/gce && \ - terraform apply -target module.etcd.null_resource.discovery_gen /cncf/gce && \ - terraform apply -target null_resource.ssl_gen /cncf/gce && \ - time terraform apply /cncf/gce && \ + terraform get ${DIR}/gce && \ + terraform apply -target module.etcd.null_resource.discovery_gen ${DIR}/gce && \ + terraform apply -target null_resource.ssl_gen ${DIR}/gce && \ + time terraform apply ${DIR}/gce && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "gce-destroy" ] ; then - time terraform destroy -force /cncf/gce + time terraform destroy -force ${DIR}/gce elif [ "$1" = "cross-cloud-deploy" ] ; then - terraform get /cncf/cross-cloud && \ - terraform apply -target module.aws.null_resource.ssl_gen /cncf/cross-cloud && \ - terraform apply -target module.gce.null_resource.ssl_gen /cncf/cross-cloud && \ - terraform apply -target module.gce.module.etcd.null_resource.discovery_gen /cncf/cross-cloud && \ - terraform apply -target module.azure.null_resource.ssl_ssh_cloud_gen /cncf/cross-cloud && \ - terraform apply -target module.azure.module.dns.null_resource.dns_gen /cncf/cross-cloud && \ - terraform apply -target module.packet.null_resource.ssl_ssh_gen /cncf/cross-cloud && \ - terraform apply -target module.packet.module.etcd.null_resource.discovery_gen /cncf/cross-cloud && \ - time terraform apply /cncf/cross-cloud && \ + terraform get ${DIR}/cross-cloud && \ + terraform apply -target module.aws.null_resource.ssl_gen ${DIR}/cross-cloud && \ + terraform apply -target module.gce.null_resource.ssl_gen ${DIR}/cross-cloud && \ + terraform apply -target module.gce.module.etcd.null_resource.discovery_gen ${DIR}/cross-cloud && \ + terraform apply -target module.azure.null_resource.ssl_ssh_cloud_gen ${DIR}/cross-cloud && \ + terraform apply -target module.azure.module.dns.null_resource.dns_gen ${DIR}/cross-cloud && \ + terraform apply -target module.packet.null_resource.ssl_ssh_gen ${DIR}/cross-cloud && \ + terraform apply -target module.packet.module.etcd.null_resource.discovery_gen ${DIR}/cross-cloud && \ + time terraform apply ${DIR}/cross-cloud && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/${name} \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} - # terraform apply -target module.azure.azurerm_resource_group.cncf /cncf/cross-cloud && \ + # terraform apply -target module.azure.azurerm_resource_group.cncf ${DIR}/cross-cloud && \ elif [ "$1" = "cross-cloud-destroy" ] ; then - time terraform destroy -force /cncf/cross-cloud + time terraform destroy -force ${DIR}/cross-cloud fi From 94781e0df155c54c1dd7a0db1096ea9122dcc670 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 25 Apr 2017 09:51:11 +1200 Subject: [PATCH 124/149] Deploy etcd keys to workers for flannel --- provisioning/packet/modules.tf | 2 ++ .../modules/worker/worker-cloud-config.yml | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/provisioning/packet/modules.tf b/provisioning/packet/modules.tf index 30d6824..e74f243 100644 --- a/provisioning/packet/modules.tf +++ b/provisioning/packet/modules.tf @@ -77,6 +77,8 @@ module "worker" { pod_cidr = "${ var.pod_cidr }" service_cidr = "${ var.service_cidr }" ca = "${file("${ var.data_dir }/.cfssl/ca.pem")}" + k8s_etcd = "${file("${ var.data_dir }/.cfssl/k8s-etcd.pem")}" + k8s_etcd_key = "${file("${ var.data_dir }/.cfssl/k8s-etcd-key.pem")}" k8s_worker = "${file("${ var.data_dir }/.cfssl/k8s-worker.pem")}" k8s_worker_key = "${file("${ var.data_dir }/.cfssl/k8s-worker-key.pem")}" etcd_discovery = "${ var.data_dir }/etcd" diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index 893ee8a..e9589d9 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -162,13 +162,25 @@ write-files: encoding: "gzip+base64" content: | ${ ca } - + + - path: /etc/kubernetes/ssl/k8s-etcd.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s_etcd } + + - path: /etc/kubernetes/ssl/k8s-etcd-key.pem + permissions: "0644" + encoding: "gzip+base64" + content: | + ${ k8s_etcd_key } + - path: /etc/kubernetes/ssl/k8s-worker.pem permissions: "0644" encoding: "gzip+base64" content: | ${ k8s_worker } - + - path: /etc/kubernetes/ssl/k8s-worker-key.pem permissions: "0644" encoding: "gzip+base64" From 228a71c72d32b936c95aed24204f87de8b65cced Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 25 Apr 2017 09:52:00 +1200 Subject: [PATCH 125/149] Remove bastion device_plan --- provisioning/packet/input.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/provisioning/packet/input.tf b/provisioning/packet/input.tf index 5fb6d87..efea988 100644 --- a/provisioning/packet/input.tf +++ b/provisioning/packet/input.tf @@ -5,7 +5,6 @@ variable "packet_project_id" {} # required for now variable "packet_facility" { default = "nrt1" } variable "packet_billing_cycle" { default = "hourly" } variable "packet_operating_system" { default = "coreos_stable" } -variable "packet_bastion_device_plan" { default = "baremetal_0" } variable "packet_master_device_plan" { default = "baremetal_0" } variable "packet_worker_device_plan" { default = "baremetal_0" } From cc573fbdb14eb77f598bab8db8837ea71ff57eaa Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Tue, 25 Apr 2017 09:53:13 +1200 Subject: [PATCH 126/149] Move k8s config files out of cloud-config.yml --- .../packet/modules/etcd/etcd-cloud-config.tf | 65 +++++++-- .../packet/modules/etcd/etcd-cloud-config.yml | 129 +++--------------- .../modules/etcd/kube-controller-manager.yml | 41 ++++++ .../packet/modules/etcd/kube-proxy.yml | 25 ++++ .../packet/modules/etcd/kube-scheduler.yml | 25 ++++ provisioning/packet/modules/worker/input.tf | 2 + .../packet/modules/worker/kube-proxy.yml | 38 ++++++ .../modules/worker/worker-cloud-config.tf | 24 ++++ .../modules/worker/worker-cloud-config.yml | 39 +----- 9 files changed, 231 insertions(+), 157 deletions(-) create mode 100644 provisioning/packet/modules/etcd/kube-controller-manager.yml create mode 100644 provisioning/packet/modules/etcd/kube-proxy.yml create mode 100644 provisioning/packet/modules/etcd/kube-scheduler.yml create mode 100644 provisioning/packet/modules/worker/kube-proxy.yml diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.tf b/provisioning/packet/modules/etcd/etcd-cloud-config.tf index 34172f4..3a88c79 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.tf +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.tf @@ -1,11 +1,58 @@ +data "template_file" "kube_apiserver" { + template = "${ file( "${ path.module }/kube-apiserver.yml" )}" + vars { + internal_tld = "${ var.internal_tld }" + service_cidr = "${ var.service_cidr }" + hyperkube = "${ var.kubelet_image_url }:${ var.kubelet_image_tag }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + } +} + +data "template_file" "kube_controller_manager" { + template = "${ file( "${ path.module }/kube-controller-manager.yml" )}" + vars { + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + } +} + +data "template_file" "kube_proxy" { + template = "${ file( "${ path.module }/kube-proxy.yml" )}" + vars { + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + } +} + +data "template_file" "kube_scheduler" { + template = "${ file( "${ path.module }/kube-scheduler.yml" )}" + vars { + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + } +} + provider "gzip" { compressionlevel = "BestCompression" } -resource "gzip_me" "kube-apiserver" { +resource "gzip_me" "kube_apiserver" { input = "${ data.template_file.kube_apiserver.rendered }" } +resource "gzip_me" "kube_controller_manager" { + input = "${ data.template_file.kube_controller_manager.rendered }" +} + +resource "gzip_me" "kube_proxy" { + input = "${ data.template_file.kube_proxy.rendered }" +} + +resource "gzip_me" "kube_scheduler" { + input = "${ data.template_file.kube_scheduler.rendered }" +} + resource "gzip_me" "ca" { input = "${ var.ca }" } @@ -26,17 +73,6 @@ resource "gzip_me" "k8s_apiserver_key" { input = "${ var.k8s_apiserver_key }" } -data "template_file" "kube_apiserver" { - template = "${ file( "${ path.module }/kube-apiserver.yml" )}" - vars { - internal_tld = "${ var.internal_tld }" - service_cidr = "${ var.service_cidr }" - hyperkube = "${ var.kubelet_image_url }:${ var.kubelet_image_tag }" - kubelet_image_url = "${ var.kubelet_image_url }" - kubelet_image_tag = "${ var.kubelet_image_tag }" - } -} - data "template_file" "etcd_user_data" { count = "${ var.master_node_count }" template = "${ file( "${ path.module }/etcd-cloud-config.yml" )}" @@ -55,7 +91,10 @@ data "template_file" "etcd_user_data" { k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" k8s_apiserver = "${ gzip_me.k8s_apiserver.output }" k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" - k8s_apiserver_yml = "${ gzip_me.kube-apiserver.output }" + k8s_apiserver_yml = "${ gzip_me.kube_apiserver.output }" + k8s_proxy_yml = "${ gzip_me.kube_proxy.output }" + k8s_scheduler = "${ gzip_me.kube_scheduler.output }" + k8s_controller_manager_yml = "${ gzip_me.kube_controller_manager.output }" etcd_discovery = "${ file(var.etcd_discovery) }" } } diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.yml b/provisioning/packet/modules/etcd/etcd-cloud-config.yml index cee6a65..a13b1ce 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.yml @@ -95,141 +95,56 @@ write-files: #!/bin/sh exec nsenter -m -u -i -n -p -t 1 -- /usr/bin/rkt "$@" + - path: /etc/logrotate.d/docker-containers + content: | + /var/lib/docker/containers/*/*.log { + rotate 7 + daily + compress + size=1M + missingok + delaycompress + copytruncate + } + - path: /etc/kubernetes/manifests/kube-apiserver.yml encoding: "gzip+base64" content: | ${ k8s_apiserver_yml } - + - path: /etc/kubernetes/manifests/kube-controller-manager.yml + encoding: "gzip+base64" content: | - apiVersion: v1 - kind: Pod - metadata: - name: kube-controller-manager - namespace: kube-system - spec: - hostNetwork: true - containers: - - name: kube-controller-manager - image: ${ kubelet_image_url }:${ kubelet_image_tag } - command: - - /hyperkube - - controller-manager - - --leader-elect=true - - --master=http://127.0.0.1:8080 - - --root-ca-file=/etc/kubernetes/ssl/ca.pem - - --service-account-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - resources: - requests: - cpu: 200m - livenessProbe: - httpGet: - host: 127.0.0.1 - path: /healthz - port: 10252 - initialDelaySeconds: 15 - timeoutSeconds: 1 - volumeMounts: - - mountPath: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - readOnly: true - - mountPath: /etc/ssl/certs - name: ssl-certs-host - readOnly: true - volumes: - - hostPath: - path: /etc/kubernetes/ssl - name: ssl-certs-kubernetes - - hostPath: - path: /usr/share/ca-certificates - name: ssl-certs-host + ${ k8s_controller_manager_yml } - path: /etc/kubernetes/manifests/kube-proxy.yml + encoding: "gzip+base64" content: | - apiVersion: v1 - kind: Pod - metadata: - name: kube-proxy - namespace: kube-system - spec: - hostNetwork: true - containers: - - name: kube-proxy - image: ${ kubelet_image_url }:${ kubelet_image_tag } - command: - - /hyperkube - - proxy - - --master=http://127.0.0.1:8080 - - --proxy-mode=iptables - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/ssl/certs - name: ssl-certs-host - readOnly: true - volumes: - - hostPath: - path: /usr/share/ca-certificates - name: ssl-certs-host + ${ k8s_proxy_yml } - path: /etc/kubernetes/manifests/kube-scheduler.yml + encoding: "gzip+base64" content: | - apiVersion: v1 - kind: Pod - metadata: - name: kube-scheduler - namespace: kube-system - spec: - hostNetwork: true - containers: - - name: kube-scheduler - image: ${ kubelet_image_url }:${ kubelet_image_tag } - command: - - /hyperkube - - scheduler - - --leader-elect=true - - --master=http://127.0.0.1:8080 - resources: - requests: - cpu: 100m - livenessProbe: - httpGet: - host: 127.0.0.1 - path: /healthz - port: 10251 - initialDelaySeconds: 15 - timeoutSeconds: 1 - - - path: /etc/logrotate.d/docker-containers - content: | - /var/lib/docker/containers/*/*.log { - rotate 7 - daily - compress - size=1M - missingok - delaycompress - copytruncate - } + ${ k8s_scheduler_yml } - path: /etc/kubernetes/ssl/ca.pem permissions: "0644" encoding: "gzip+base64" content: | ${ ca } - + - path: /etc/kubernetes/ssl/k8s-etcd.pem permissions: "0644" encoding: "gzip+base64" content: | ${ k8s_etcd } - + - path: /etc/kubernetes/ssl/k8s-etcd-key.pem permissions: "0644" encoding: "gzip+base64" content: | ${ k8s_etcd_key } - + - path: /etc/kubernetes/ssl/k8s-apiserver.pem permissions: "0644" encoding: "gzip+base64" diff --git a/provisioning/packet/modules/etcd/kube-controller-manager.yml b/provisioning/packet/modules/etcd/kube-controller-manager.yml new file mode 100644 index 0000000..0d34cdc --- /dev/null +++ b/provisioning/packet/modules/etcd/kube-controller-manager.yml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Pod +metadata: + name: kube-controller-manager + namespace: kube-system +spec: + hostNetwork: true + containers: + - name: kube-controller-manager + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - controller-manager + - --leader-elect=true + - --master=http://127.0.0.1:8080 + - --root-ca-file=/etc/kubernetes/ssl/ca.pem + - --service-account-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + resources: + requests: + cpu: 200m + livenessProbe: + httpGet: + host: 127.0.0.1 + path: /healthz + port: 10252 + initialDelaySeconds: 15 + timeoutSeconds: 1 + volumeMounts: + - mountPath: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + readOnly: true + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/ssl + name: ssl-certs-kubernetes + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host diff --git a/provisioning/packet/modules/etcd/kube-proxy.yml b/provisioning/packet/modules/etcd/kube-proxy.yml new file mode 100644 index 0000000..2cc43a3 --- /dev/null +++ b/provisioning/packet/modules/etcd/kube-proxy.yml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Pod +metadata: + name: kube-proxy + namespace: kube-system +spec: + hostNetwork: true + containers: + - name: kube-proxy + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - proxy + - --master=http://127.0.0.1:8080 + - --proxy-mode=iptables + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/ssl/certs + name: ssl-certs-host + readOnly: true + volumes: + - hostPath: + path: /usr/share/ca-certificates + name: ssl-certs-host diff --git a/provisioning/packet/modules/etcd/kube-scheduler.yml b/provisioning/packet/modules/etcd/kube-scheduler.yml new file mode 100644 index 0000000..15f50f6 --- /dev/null +++ b/provisioning/packet/modules/etcd/kube-scheduler.yml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Pod +metadata: + name: kube-scheduler + namespace: kube-system +spec: + hostNetwork: true + containers: + - name: kube-scheduler + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - scheduler + - --leader-elect=true + - --master=http://127.0.0.1:8080 + resources: + requests: + cpu: 100m + livenessProbe: + httpGet: + host: 127.0.0.1 + path: /healthz + port: 10251 + initialDelaySeconds: 15 + timeoutSeconds: 1 diff --git a/provisioning/packet/modules/worker/input.tf b/provisioning/packet/modules/worker/input.tf index fbb88a9..1357e64 100644 --- a/provisioning/packet/modules/worker/input.tf +++ b/provisioning/packet/modules/worker/input.tf @@ -13,6 +13,8 @@ variable "internal_tld" {} variable "pod_cidr" {} variable "service_cidr" {} variable "ca" {} +variable "k8s_etcd" {} +variable "k8s_etcd_key" {} variable "k8s_worker" {} variable "k8s_worker_key" {} variable "data_dir" {} diff --git a/provisioning/packet/modules/worker/kube-proxy.yml b/provisioning/packet/modules/worker/kube-proxy.yml new file mode 100644 index 0000000..f1284ae --- /dev/null +++ b/provisioning/packet/modules/worker/kube-proxy.yml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: Pod +metadata: + name: kube-proxy + namespace: kube-system +spec: + hostNetwork: true + containers: + - name: kube-proxy + image: ${ kubelet_image_url }:${ kubelet_image_tag } + command: + - /hyperkube + - proxy + - --kubeconfig=/etc/kubernetes/kubeconfig.yml + - --master=https://master.${ internal_tld } + - --proxy-mode=iptables + securityContext: + privileged: true + volumeMounts: + - mountPath: /etc/ssl/certs + name: "ssl-certs" + - mountPath: /etc/kubernetes/kubeconfig.yml + name: "kubeconfig" + readOnly: true + - mountPath: /etc/kubernetes/ssl + name: "etc-kube-ssl" + readOnly: true + volumes: + - name: "ssl-certs" + hostPath: + path: "/usr/share/ca-certificates" + - name: "kubeconfig" + hostPath: + path: "/etc/kubernetes/kubeconfig.yml" + - name: "etc-kube-ssl" + hostPath: + path: "/etc/kubernetes/ssl" + diff --git a/provisioning/packet/modules/worker/worker-cloud-config.tf b/provisioning/packet/modules/worker/worker-cloud-config.tf index 5edac7a..f71acc0 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.tf +++ b/provisioning/packet/modules/worker/worker-cloud-config.tf @@ -1,11 +1,32 @@ +data "template_file" "kube_proxy" { + template = "${ file( "${ path.module }/kube-proxy.yml" )}" + vars { + internal_tld = "${ var.internal_tld }" + kubelet_image_url = "${ var.kubelet_image_url }" + kubelet_image_tag = "${ var.kubelet_image_tag }" + } +} + provider "gzip" { compressionlevel = "BestCompression" } +resource "gzip_me" "kube_proxy" { + input = "${ data.template_file.kube_proxy.rendered }" +} + resource "gzip_me" "ca" { input = "${ var.ca }" } +resource "gzip_me" "k8s_etcd" { + input = "${ var.k8s_etcd }" +} + +resource "gzip_me" "k8s_etcd_key" { + input = "${ var.k8s_etcd_key }" +} + resource "gzip_me" "k8s_worker" { input = "${ var.k8s_worker }" } @@ -23,7 +44,10 @@ data "template_file" "worker_user_data" { dns_service_ip = "${ var.dns_service_ip }" kubelet_image_url = "${ var.kubelet_image_url }" kubelet_image_tag = "${ var.kubelet_image_tag }" + k8s_proxy_yml = "${ gzip_me.kube_proxy.output }" ca = "${ gzip_me.ca.output }" + k8s_etcd = "${ gzip_me.k8s_etcd.output }" + k8s_etcd_key = "${ gzip_me.k8s_etcd_key.output }" k8s_worker = "${ gzip_me.k8s_worker.output }" k8s_worker_key = "${ gzip_me.k8s_worker_key.output }" etcd_discovery = "${file(var.etcd_discovery)}" diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index e9589d9..89d6e30 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -106,44 +106,9 @@ write-files: current-context: kubelet-context - path: /etc/kubernetes/manifests/kube-proxy.yml + encoding: "gzip+base64" content: | - apiVersion: v1 - kind: Pod - metadata: - name: kube-proxy - namespace: kube-system - spec: - hostNetwork: true - containers: - - name: kube-proxy - image: ${ kubelet_image_url }:${ kubelet_image_tag } - command: - - /hyperkube - - proxy - - --kubeconfig=/etc/kubernetes/kubeconfig.yml - - --master=https://master.${ internal_tld } - - --proxy-mode=iptables - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/ssl/certs - name: "ssl-certs" - - mountPath: /etc/kubernetes/kubeconfig.yml - name: "kubeconfig" - readOnly: true - - mountPath: /etc/kubernetes/ssl - name: "etc-kube-ssl" - readOnly: true - volumes: - - name: "ssl-certs" - hostPath: - path: "/usr/share/ca-certificates" - - name: "kubeconfig" - hostPath: - path: "/etc/kubernetes/kubeconfig.yml" - - name: "etc-kube-ssl" - hostPath: - path: "/etc/kubernetes/ssl" + ${ k8s_proxy_yml } - path: /etc/logrotate.d/docker-containers content: | From ded6428c0d1ebceca29c36bb2d7e6e31bca18538 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 04:15:46 +1200 Subject: [PATCH 127/149] Make apiserver bind to 0.0.0.0 --- provisioning/packet/modules/etcd/kube-apiserver.yml | 2 +- provisioning/packet/modules/worker/worker-cloud-config.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioning/packet/modules/etcd/kube-apiserver.yml b/provisioning/packet/modules/etcd/kube-apiserver.yml index c85a1f5..bcd02c2 100644 --- a/provisioning/packet/modules/etcd/kube-apiserver.yml +++ b/provisioning/packet/modules/etcd/kube-apiserver.yml @@ -22,8 +22,8 @@ spec: - --cloud-provider=azure - --cloud-config=/etc/kubernetes/ssl/azure-config.json - --etcd-servers=http://etcd.${ internal_tld }:2379 - - --insecure-bind-address=0.0.0.0 - --secure-port=443 + - --bind-address=0.0.0.0 - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - --service-cluster-ip-range=${ service_cidr } - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index 89d6e30..9ce3c07 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -61,7 +61,7 @@ coreos: ExecStartPre=/usr/bin/mount --make-shared /var/lib/kubelet ExecStart=/usr/lib/coreos/kubelet-wrapper \ --allow-privileged=true \ - --api-servers=http://master.${ internal_tld }:8080 \ + --api-servers=https://master.${ internal_tld } \ --cluster-dns=${ dns_service_ip } \ --cluster_domain=${ cluster_domain } \ --config=/etc/kubernetes/manifests \ From fb4820a0cae00568ccdd1b3f1219e985b13a0ec4 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 04:16:37 +1200 Subject: [PATCH 128/149] Use packet location SJC1 --- provisioning/packet/input.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provisioning/packet/input.tf b/provisioning/packet/input.tf index efea988..f75f284 100644 --- a/provisioning/packet/input.tf +++ b/provisioning/packet/input.tf @@ -2,7 +2,8 @@ variable "name" { default = "packet" } # Set with env TF_VAR_packet_project_id variable "packet_project_id" {} # required for now -variable "packet_facility" { default = "nrt1" } +# https://www.packet.net/locations/ +variable "packet_facility" { default = "sjc1" } variable "packet_billing_cycle" { default = "hourly" } variable "packet_operating_system" { default = "coreos_stable" } variable "packet_master_device_plan" { default = "baremetal_0" } From 4370e1146ee7eb83882052b5d3426ddbef735ca6 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 04:17:15 +1200 Subject: [PATCH 129/149] Update to scheduler_yml --- provisioning/packet/modules/etcd/etcd-cloud-config.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.tf b/provisioning/packet/modules/etcd/etcd-cloud-config.tf index 3a88c79..b9a7498 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.tf +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.tf @@ -93,7 +93,7 @@ data "template_file" "etcd_user_data" { k8s_apiserver_key = "${ gzip_me.k8s_apiserver_key.output }" k8s_apiserver_yml = "${ gzip_me.kube_apiserver.output }" k8s_proxy_yml = "${ gzip_me.kube_proxy.output }" - k8s_scheduler = "${ gzip_me.kube_scheduler.output }" + k8s_scheduler_yml = "${ gzip_me.kube_scheduler.output }" k8s_controller_manager_yml = "${ gzip_me.kube_controller_manager.output }" etcd_discovery = "${ file(var.etcd_discovery) }" } From af38aa5f6108e18df0c8f2bbcb5e6317bb8998fd Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 05:20:15 +1200 Subject: [PATCH 130/149] Add flannel_docker_opts.env to docker.service --- provisioning/packet/modules/etcd/etcd-cloud-config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.yml b/provisioning/packet/modules/etcd/etcd-cloud-config.yml index a13b1ce..1cfefc2 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.yml @@ -48,6 +48,7 @@ coreos: content: | [Service] Environment="DOCKER_OPTS=--storage-driver=overlay" + EnvironmentFile=/run/flannel/flannel_docker_opts.env - name: kubelet.service command: start From befd3c883bab7eed97fcb8fbebe90a0842039469 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 05:48:39 +1200 Subject: [PATCH 131/149] Remove flannel config via cloud-init --- .../packet/modules/etcd/etcd-cloud-config.yml | 20 ------------------- .../modules/worker/worker-cloud-config.yml | 18 ----------------- 2 files changed, 38 deletions(-) diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.yml b/provisioning/packet/modules/etcd/etcd-cloud-config.yml index 1cfefc2..96ff369 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.yml @@ -22,33 +22,13 @@ coreos: - name: etcd2.service command: start - - name: flanneld.service - command: start - drop-ins: - - name: 50-network-config.conf - content: | - [Service] - ExecStartPre=-/usr/bin/etcdctl mk /coreos.com/network/config \ - '{ "Network": "${ pod_cidr }", "Backend": { "Type": "vxlan" } }' - Restart=always - RestartSec=10 - - name: docker.service command: start drop-ins: - - name: 40-flannel.conf - content: | - [Unit] - After=flanneld.service - Requires=flanneld.service - [Service] - Restart=always - RestartSec=10 - name: overlay.conf content: | [Service] Environment="DOCKER_OPTS=--storage-driver=overlay" - EnvironmentFile=/run/flannel/flannel_docker_opts.env - name: kubelet.service command: start diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index 9ce3c07..0e9bc4f 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -15,26 +15,8 @@ coreos: - name: etcd2.service command: start - - name: flanneld.service - command: start - drop-ins: - - name: 50-network-config.conf - content: | - [Service] - Restart=always - RestartSec=10 - - name: docker.service command: start - drop-ins: - - name: 40-flannel.conf - content: | - [Unit] - After=flanneld.service - Requires=flanneld.service - [Service] - Restart=always - RestartSec=10 - name: kubelet.service command: start From d90bc5eae84ebde32b651598f7c7af3d55f90ad7 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 10:15:59 +1200 Subject: [PATCH 132/149] Bump to kubelet_image_tag 1.6.2_coreos.0 --- provisioning/packet/input.tf | 2 +- provisioning/packet/modules/etcd/etcd-cloud-config.yml | 4 ++-- provisioning/packet/modules/etcd/kube-apiserver.yml | 2 -- provisioning/packet/modules/worker/worker-cloud-config.yml | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/provisioning/packet/input.tf b/provisioning/packet/input.tf index f75f284..34d76e5 100644 --- a/provisioning/packet/input.tf +++ b/provisioning/packet/input.tf @@ -31,4 +31,4 @@ variable "worker_node_count" { default = "3" } # Hyperkube # Set from https://quay.io/repository/coreos/hyperkube variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} -variable "kubelet_image_tag" { default = "v1.4.7_coreos.0"} +variable "kubelet_image_tag" { default = "v1.6.2_coreos.0"} diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.yml b/provisioning/packet/modules/etcd/etcd-cloud-config.yml index 96ff369..2935c46 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.yml @@ -38,7 +38,7 @@ coreos: [Service] Environment="KUBELET_IMAGE_URL=${ kubelet_image_url }" Environment="KUBELET_IMAGE_TAG=${ kubelet_image_tag }" - Environment="RKT_OPTS=\ + Environment="RKT_RUN_ARGS=\ --volume dns,kind=host,source=/etc/resolv.conf \ --mount volume=dns,target=/etc/resolv.conf \ --volume rkt,kind=host,source=/opt/bin/host-rkt \ @@ -58,7 +58,7 @@ coreos: --api-servers=http://127.0.0.1:8080 \ --cluster-dns=${ dns_service_ip } \ --cluster_domain=${ cluster_domain } \ - --config=/etc/kubernetes/manifests \ + --pod-manifest-path=/etc/kubernetes/manifests \ --register-schedulable=false Restart=always RestartSec=5 diff --git a/provisioning/packet/modules/etcd/kube-apiserver.yml b/provisioning/packet/modules/etcd/kube-apiserver.yml index bcd02c2..5089f26 100644 --- a/provisioning/packet/modules/etcd/kube-apiserver.yml +++ b/provisioning/packet/modules/etcd/kube-apiserver.yml @@ -19,8 +19,6 @@ spec: - --admission-control=ServiceAccount - --allow-privileged=true - --client-ca-file=/etc/kubernetes/ssl/ca.pem - - --cloud-provider=azure - - --cloud-config=/etc/kubernetes/ssl/azure-config.json - --etcd-servers=http://etcd.${ internal_tld }:2379 - --secure-port=443 - --bind-address=0.0.0.0 diff --git a/provisioning/packet/modules/worker/worker-cloud-config.yml b/provisioning/packet/modules/worker/worker-cloud-config.yml index 0e9bc4f..182fc08 100644 --- a/provisioning/packet/modules/worker/worker-cloud-config.yml +++ b/provisioning/packet/modules/worker/worker-cloud-config.yml @@ -46,8 +46,8 @@ coreos: --api-servers=https://master.${ internal_tld } \ --cluster-dns=${ dns_service_ip } \ --cluster_domain=${ cluster_domain } \ - --config=/etc/kubernetes/manifests \ --kubeconfig=/etc/kubernetes/kubeconfig.yml \ + --pod-manifest-path=/etc/kubernetes/manifests \ --register-node=true \ --tls-cert-file=/etc/kubernetes/ssl/k8s-worker.pem \ --tls-private-key-file=/etc/kubernetes/ssl/k8s-worker-key.pem From 12360037b0053b2888251414a2d0d2609d7c65ec Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 10:17:18 +1200 Subject: [PATCH 133/149] Remove register-schedulable (no longer valid) --- provisioning/azure/modules/etcd/etcd-cloud-config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/provisioning/azure/modules/etcd/etcd-cloud-config.yml b/provisioning/azure/modules/etcd/etcd-cloud-config.yml index 5fe6e0a..3b0fb83 100644 --- a/provisioning/azure/modules/etcd/etcd-cloud-config.yml +++ b/provisioning/azure/modules/etcd/etcd-cloud-config.yml @@ -62,8 +62,7 @@ coreos: --cloud-config=/etc/kubernetes/ssl/azure-config.json \ --cluster-dns=${ dns_service_ip } \ --cluster_domain=${ cluster_domain } \ - --config=/etc/kubernetes/manifests \ - --register-schedulable=false + --config=/etc/kubernetes/manifests Restart=always RestartSec=5 [Install] From 8048a82b18dae08d4ea3b3d12e78261cb73024c5 Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 10:17:47 +1200 Subject: [PATCH 134/149] Add masterX.public.DEPLOY.cncf.ci DNS record --- provisioning/packet/modules/dns/dns.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/packet/modules/dns/dns.tf b/provisioning/packet/modules/dns/dns.tf index fe57011..452a345 100644 --- a/provisioning/packet/modules/dns/dns.tf +++ b/provisioning/packet/modules/dns/dns.tf @@ -52,7 +52,7 @@ resource "dnsimple_record" "A-public-endpoint" { resource "dnsimple_record" "A-public-masters" { count = "${ var.master_node_count }" - name = "master${ count.index + 1 }.${ var.name }" + name = "master${ count.index + 1 }.public.${ var.name }" value = "${ element(var.public_master_ips, count.index) }" type = "A" ttl = "${ var.record_ttl }" From 5f35bfc1be898b3913e3204816f8e386e6fa56ed Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Wed, 26 Apr 2017 10:18:26 +1200 Subject: [PATCH 135/149] Update kube-apiserver/proxy config to k8s 1.6.2 --- .../packet/modules/etcd/etcd-cloud-config.tf | 1 + .../packet/modules/etcd/kube-apiserver.yml | 21 ++++++++++++------- .../packet/modules/etcd/kube-proxy.yml | 7 ++++++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/provisioning/packet/modules/etcd/etcd-cloud-config.tf b/provisioning/packet/modules/etcd/etcd-cloud-config.tf index b9a7498..9b7476e 100644 --- a/provisioning/packet/modules/etcd/etcd-cloud-config.tf +++ b/provisioning/packet/modules/etcd/etcd-cloud-config.tf @@ -3,6 +3,7 @@ data "template_file" "kube_apiserver" { vars { internal_tld = "${ var.internal_tld }" service_cidr = "${ var.service_cidr }" + master_node_count = "${ var.master_node_count }" hyperkube = "${ var.kubelet_image_url }:${ var.kubelet_image_tag }" kubelet_image_url = "${ var.kubelet_image_url }" kubelet_image_tag = "${ var.kubelet_image_tag }" diff --git a/provisioning/packet/modules/etcd/kube-apiserver.yml b/provisioning/packet/modules/etcd/kube-apiserver.yml index 5089f26..4fff647 100644 --- a/provisioning/packet/modules/etcd/kube-apiserver.yml +++ b/provisioning/packet/modules/etcd/kube-apiserver.yml @@ -11,18 +11,25 @@ spec: command: - /hyperkube - apiserver - - --admission-control=LimitRanger - - --admission-control=NamespaceExists - --admission-control=NamespaceLifecycle - - --admission-control=ResourceQuota - - --admission-control=SecurityContextDeny + - --admission-control=LimitRanger - --admission-control=ServiceAccount + - --admission-control=DefaultStorageClass + - --admission-control=ResourceQuota + - --advertise-address=$private_ipv4 + - --apiserver-count=${ master_node_count } - --allow-privileged=true + - --anonymous-auth=false - --client-ca-file=/etc/kubernetes/ssl/ca.pem - - --etcd-servers=http://etcd.${ internal_tld }:2379 + - --enable-swagger-ui + - --etcd-cafile=/etc/kubernetes/ssl/ca.pem + - --etcd-certfile=/etc/kubernetes/ssl/k8s-etcd.pem + - --etcd-keyfile=/etc/kubernetes/ssl/k8s-etcd-key.pem + - --runtime-config=extensions/v1beta1/networkpolicies=true,batch/v2alpha1 + - --etcd-servers=https://etcd.${ internal-tld }:2379 - --secure-port=443 - - --bind-address=0.0.0.0 - - --service-account-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem + - --service-account-lookup + - --service-account-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem - --service-cluster-ip-range=${ service_cidr } - --tls-cert-file=/etc/kubernetes/ssl/k8s-apiserver.pem - --tls-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem diff --git a/provisioning/packet/modules/etcd/kube-proxy.yml b/provisioning/packet/modules/etcd/kube-proxy.yml index 2cc43a3..a71a4eb 100644 --- a/provisioning/packet/modules/etcd/kube-proxy.yml +++ b/provisioning/packet/modules/etcd/kube-proxy.yml @@ -12,14 +12,19 @@ spec: - /hyperkube - proxy - --master=http://127.0.0.1:8080 - - --proxy-mode=iptables securityContext: privileged: true volumeMounts: - mountPath: /etc/ssl/certs name: ssl-certs-host readOnly: true + - mountPath: /var/run/dbus + name: dbus + readOnly: false volumes: - hostPath: path: /usr/share/ca-certificates name: ssl-certs-host + - hostPath: + path: /var/run/dbus + name: dbus From 0176ad02c90f8ffaa588aa1ef2afa86db768c77f Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 27 Apr 2017 10:09:49 +1200 Subject: [PATCH 136/149] Update Dockerfile to use to Correct Working DIR --- provisioning/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/provisioning/Dockerfile b/provisioning/Dockerfile index 3cf162a..1c22ce9 100644 --- a/provisioning/Dockerfile +++ b/provisioning/Dockerfile @@ -48,10 +48,8 @@ RUN go get -u github.com/jakexks/terraform-provider-gzip && \ #Add Terraform Modules -COPY aws /aws/ -COPY azure /azure/ COPY provision.sh /cncf/ RUN chmod +x /cncf/provision.sh ENTRYPOINT ["/cncf/provision.sh"] -WORKDIR /cncf/data +WORKDIR /cncf/ CMD ["aws-deploy"] From 1861b021330faf60bac2b89ed863120d28373e49 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 27 Apr 2017 10:10:27 +1200 Subject: [PATCH 137/149] Fix Terraform Path for AWS Deploy --- provisioning/provision.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/provisioning/provision.sh b/provisioning/provision.sh index 6c5e9a0..f9c264e 100755 --- a/provisioning/provision.sh +++ b/provisioning/provision.sh @@ -16,12 +16,11 @@ cd $TF_VAR_data_dir if [ "$1" = "aws-deploy" ] ; then terraform get ${DIR}/aws && \ terraform apply -target null_resource.ssl_gen ${DIR}/aws && \ - time terraform apply /deploy/aws && \ + time terraform apply ${DIR}/aws && \ printf "${RED}\n#Commands to Configue Kubectl \n\n" && \ printf 'sudo chown -R $(whoami):$(whoami) $(pwd)/data/ \n\n' && \ printf 'export KUBECONFIG=$(pwd)/data/kubeconfig \n\n'${NC} elif [ "$1" = "aws-destroy" ] ; then - write_aws_config time terraform destroy -force ${DIR}/aws elif [ "$1" = "azure-deploy" ] ; then # There are some dependency issues around cert,sshkey,k8s_cloud_config, and dns From 334f87b44413a086107fb2a8c415c18f4bcd9297 Mon Sep 17 00:00:00 2001 From: DLX Date: Thu, 27 Apr 2017 10:11:20 +1200 Subject: [PATCH 138/149] First Attemt at GKE --- provisioning/gke/input.tf | 10 +++++++ provisioning/gke/modules.tf | 21 +++++++++++++ provisioning/gke/modules/cluster/cluster.tf | 30 +++++++++++++++++++ provisioning/gke/modules/cluster/input.tf | 12 ++++++++ provisioning/gke/modules/cluster/node-pool.tf | 7 +++++ provisioning/gke/modules/vpc/gce-subnet.tf | 6 ++++ provisioning/gke/modules/vpc/input.tf | 3 ++ provisioning/gke/modules/vpc/output.tf | 2 ++ provisioning/gke/modules/vpc/vpc.tf | 4 +++ 9 files changed, 95 insertions(+) create mode 100644 provisioning/gke/input.tf create mode 100644 provisioning/gke/modules.tf create mode 100644 provisioning/gke/modules/cluster/cluster.tf create mode 100644 provisioning/gke/modules/cluster/input.tf create mode 100644 provisioning/gke/modules/cluster/node-pool.tf create mode 100644 provisioning/gke/modules/vpc/gce-subnet.tf create mode 100644 provisioning/gke/modules/vpc/input.tf create mode 100644 provisioning/gke/modules/vpc/output.tf create mode 100644 provisioning/gke/modules/vpc/vpc.tf diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf new file mode 100644 index 0000000..3a3ae9a --- /dev/null +++ b/provisioning/gke/input.tf @@ -0,0 +1,10 @@ +variable "name" { default = "test" } +variable "region" { default = "us-central1" } +variable "zone" { default = "us-central1-a" } +variable "project" { default = "test-163823" } +variable "cidr" { default = "10.0.0.0/16" } +variable "node_count" { default = "3" } +variable "node_version" { default = "1.6.2" } +variable "master_user" { default = "aoeui" } +variable "master_password" { default = "iueoadhtns"} +variable "vm_size" { default = "g1-small"} diff --git a/provisioning/gke/modules.tf b/provisioning/gke/modules.tf new file mode 100644 index 0000000..dbaf59f --- /dev/null +++ b/provisioning/gke/modules.tf @@ -0,0 +1,21 @@ +module "vpc" { + source = "./modules/vpc" + name = "${ var.name }" + cidr = "${ var.cidr }" + region = "${ var.region }" +} + +module "cluster" { + source = "./modules/cluster" + name = "${ var.name }" + region = "${ var.region }" + zone = "${ var.zone }" + project = "${ var.project}" + node_count = "${ var.node_count }" + network = "${ module.vpc.network }" + subnetwork = "${ module.vpc.subnetwork }" + node_version = "${ var.node_version }" + master_user = "${ var.master_user }" + master_password = ${ var.master_password } + vm_size = ${ var.vm_size } +} diff --git a/provisioning/gke/modules/cluster/cluster.tf b/provisioning/gke/modules/cluster/cluster.tf new file mode 100644 index 0000000..aba6a31 --- /dev/null +++ b/provisioning/gke/modules/cluster/cluster.tf @@ -0,0 +1,30 @@ +resource "google_container_cluster" "cncf" { + name = "${ var.name }" + zone = "${ var.zone }" + project = "${ var.project }" + initial_node_count = "${ var.node_count }" + + additional_zones = [ + "us-central1-b", + "us-central1-c", + ] + + network = ${ var.network } + subnetwork = ${ var.subnetwork } + node_version = ${ var.node_version } + + master_auth { + username = "${ var.master_user }" + password = "${ var.master_password }" + } + + node_config { + machine_type = ${ var.vm_size } + oauth_scopes = [ + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + ] + } +} diff --git a/provisioning/gke/modules/cluster/input.tf b/provisioning/gke/modules/cluster/input.tf new file mode 100644 index 0000000..caa9995 --- /dev/null +++ b/provisioning/gke/modules/cluster/input.tf @@ -0,0 +1,12 @@ +variable "name" {} +variable "region" {} +variable "zone" {} +variable "project" {} +variable "node_count" {} +variable "network" {} +variable "subnetwork" {} +variable "node_version" {} +variable "master_user" {} +variable "master_password" {} +variable "vm_size" {} + diff --git a/provisioning/gke/modules/cluster/node-pool.tf b/provisioning/gke/modules/cluster/node-pool.tf new file mode 100644 index 0000000..bcd8768 --- /dev/null +++ b/provisioning/gke/modules/cluster/node-pool.tf @@ -0,0 +1,7 @@ +resource "google_container_node_pool" "cncf" { + name = "${ var.name }" + project = "${ var.project }" + zone = "${ var.zone }" + cluster = "${google_container_cluster.cncf.name}" + initial_node_count = "${ var.pool_size }" +} diff --git a/provisioning/gke/modules/vpc/gce-subnet.tf b/provisioning/gke/modules/vpc/gce-subnet.tf new file mode 100644 index 0000000..49ac226 --- /dev/null +++ b/provisioning/gke/modules/vpc/gce-subnet.tf @@ -0,0 +1,6 @@ +resource "google_compute_subnetwork" "cncf" { + name = "${ var.name }" + ip_cidr_range = "${ var.cidr }" + network = "${ google_compute_network.cncf.self_link }" + region = "${ var.region }" +} diff --git a/provisioning/gke/modules/vpc/input.tf b/provisioning/gke/modules/vpc/input.tf new file mode 100644 index 0000000..e523c3e --- /dev/null +++ b/provisioning/gke/modules/vpc/input.tf @@ -0,0 +1,3 @@ +variable "cidr" {} +variable "name" {} +variable "region" {} diff --git a/provisioning/gke/modules/vpc/output.tf b/provisioning/gke/modules/vpc/output.tf new file mode 100644 index 0000000..f6b4534 --- /dev/null +++ b/provisioning/gke/modules/vpc/output.tf @@ -0,0 +1,2 @@ +output "network" { value = "${ google_compute_network.cncf.self_link }" } +output "subnetwork" { value = "${ google_compute_subnetwork.cncf.self_link }" } diff --git a/provisioning/gke/modules/vpc/vpc.tf b/provisioning/gke/modules/vpc/vpc.tf new file mode 100644 index 0000000..7394480 --- /dev/null +++ b/provisioning/gke/modules/vpc/vpc.tf @@ -0,0 +1,4 @@ +resource "google_compute_network" "cncf" { + name = "${ var.name }" + auto_create_subnetworks = "false" +} From dfbe11ba0954329cb1449cf9f1d71e3f13af4a62 Mon Sep 17 00:00:00 2001 From: DLX Date: Fri, 28 Apr 2017 04:44:19 +1200 Subject: [PATCH 139/149] gke node size and pool count changes --- provisioning/gke/input.tf | 4 +++- provisioning/gke/modules.tf | 5 +++-- provisioning/gke/modules/cluster/cluster.tf | 16 ++++++++-------- provisioning/gke/modules/cluster/input.tf | 1 + provisioning/gke/modules/cluster/node-pool.tf | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf index 3a3ae9a..e240780 100644 --- a/provisioning/gke/input.tf +++ b/provisioning/gke/input.tf @@ -7,4 +7,6 @@ variable "node_count" { default = "3" } variable "node_version" { default = "1.6.2" } variable "master_user" { default = "aoeui" } variable "master_password" { default = "iueoadhtns"} -variable "vm_size" { default = "g1-small"} +variable "vm_size" { default = "n1-standard-1"} +variable "node_pool_count" { default = "3"} + diff --git a/provisioning/gke/modules.tf b/provisioning/gke/modules.tf index dbaf59f..4d7e316 100644 --- a/provisioning/gke/modules.tf +++ b/provisioning/gke/modules.tf @@ -16,6 +16,7 @@ module "cluster" { subnetwork = "${ module.vpc.subnetwork }" node_version = "${ var.node_version }" master_user = "${ var.master_user }" - master_password = ${ var.master_password } - vm_size = ${ var.vm_size } + master_password = "${ var.master_password }" + vm_size = "${ var.vm_size }" + node_pool_count = "${ var.node_pool_count }" } diff --git a/provisioning/gke/modules/cluster/cluster.tf b/provisioning/gke/modules/cluster/cluster.tf index aba6a31..8ca7363 100644 --- a/provisioning/gke/modules/cluster/cluster.tf +++ b/provisioning/gke/modules/cluster/cluster.tf @@ -4,23 +4,23 @@ resource "google_container_cluster" "cncf" { project = "${ var.project }" initial_node_count = "${ var.node_count }" - additional_zones = [ + additional_zones = [ "us-central1-b", "us-central1-c", ] - network = ${ var.network } - subnetwork = ${ var.subnetwork } - node_version = ${ var.node_version } + network = "${ var.network }" + subnetwork = "${ var.subnetwork }" + node_version = "${ var.node_version }" master_auth { - username = "${ var.master_user }" - password = "${ var.master_password }" + username = "${ var.master_user }" + password = "${ var.master_password }" } node_config { - machine_type = ${ var.vm_size } - oauth_scopes = [ + machine_type = "${ var.vm_size }" + oauth_scopes = [ "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/logging.write", diff --git a/provisioning/gke/modules/cluster/input.tf b/provisioning/gke/modules/cluster/input.tf index caa9995..7ab1f87 100644 --- a/provisioning/gke/modules/cluster/input.tf +++ b/provisioning/gke/modules/cluster/input.tf @@ -8,5 +8,6 @@ variable "subnetwork" {} variable "node_version" {} variable "master_user" {} variable "master_password" {} +variable "node_pool_count" {} variable "vm_size" {} diff --git a/provisioning/gke/modules/cluster/node-pool.tf b/provisioning/gke/modules/cluster/node-pool.tf index bcd8768..1eacac8 100644 --- a/provisioning/gke/modules/cluster/node-pool.tf +++ b/provisioning/gke/modules/cluster/node-pool.tf @@ -3,5 +3,5 @@ resource "google_container_node_pool" "cncf" { project = "${ var.project }" zone = "${ var.zone }" cluster = "${google_container_cluster.cncf.name}" - initial_node_count = "${ var.pool_size }" + initial_node_count = "${ var.node_pool_count }" } From 7a95816a7f91b93e128b64d25835bd99033176dd Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Fri, 28 Apr 2017 04:50:05 +1200 Subject: [PATCH 140/149] Updating gku defaults and provision.sh --- provisioning/gke/input.tf | 6 +++--- provisioning/provision.sh | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf index e240780..b90879f 100644 --- a/provisioning/gke/input.tf +++ b/provisioning/gke/input.tf @@ -1,12 +1,12 @@ -variable "name" { default = "test" } +variable "name" { default = "gkecluster" } variable "region" { default = "us-central1" } variable "zone" { default = "us-central1-a" } variable "project" { default = "test-163823" } variable "cidr" { default = "10.0.0.0/16" } variable "node_count" { default = "3" } variable "node_version" { default = "1.6.2" } -variable "master_user" { default = "aoeui" } -variable "master_password" { default = "iueoadhtns"} +variable "master_user" { default = "cncf" } +variable "master_password" { default = "demo"} variable "vm_size" { default = "n1-standard-1"} variable "node_pool_count" { default = "3"} diff --git a/provisioning/provision.sh b/provisioning/provision.sh index f9c264e..af2f279 100755 --- a/provisioning/provision.sh +++ b/provisioning/provision.sh @@ -55,6 +55,11 @@ elif [ "$1" = "gce-deploy" ] ; then printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "gce-destroy" ] ; then time terraform destroy -force ${DIR}/gce +eelif [ "$1" = "gke-deploy" ] ; then + terraform get ${DIR}/gke && \ + time terraform apply ${DIR}/gke +elif [ "$1" = "gke-destroy" ] ; then + time terraform destroy -force ${DIR}/gke elif [ "$1" = "cross-cloud-deploy" ] ; then terraform get ${DIR}/cross-cloud && \ terraform apply -target module.aws.null_resource.ssl_gen ${DIR}/cross-cloud && \ From 4a4828eafa4449a9439641996f00986aa1fb483e Mon Sep 17 00:00:00 2001 From: DLX Date: Fri, 28 Apr 2017 05:57:21 +1200 Subject: [PATCH 141/149] Fix Network for GKE --- provisioning/gke/input.tf | 2 +- provisioning/gke/modules.tf | 4 ++-- provisioning/gke/modules/vpc/output.tf | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf index b90879f..4354f00 100644 --- a/provisioning/gke/input.tf +++ b/provisioning/gke/input.tf @@ -4,7 +4,7 @@ variable "zone" { default = "us-central1-a" } variable "project" { default = "test-163823" } variable "cidr" { default = "10.0.0.0/16" } variable "node_count" { default = "3" } -variable "node_version" { default = "1.6.2" } +variable "node_version" { default = "1.5.6" } variable "master_user" { default = "cncf" } variable "master_password" { default = "demo"} variable "vm_size" { default = "n1-standard-1"} diff --git a/provisioning/gke/modules.tf b/provisioning/gke/modules.tf index 4d7e316..695003f 100644 --- a/provisioning/gke/modules.tf +++ b/provisioning/gke/modules.tf @@ -12,8 +12,8 @@ module "cluster" { zone = "${ var.zone }" project = "${ var.project}" node_count = "${ var.node_count }" - network = "${ module.vpc.network }" - subnetwork = "${ module.vpc.subnetwork }" + network = "${ var.name }" + subnetwork = "${ var.name }" node_version = "${ var.node_version }" master_user = "${ var.master_user }" master_password = "${ var.master_password }" diff --git a/provisioning/gke/modules/vpc/output.tf b/provisioning/gke/modules/vpc/output.tf index f6b4534..e69de29 100644 --- a/provisioning/gke/modules/vpc/output.tf +++ b/provisioning/gke/modules/vpc/output.tf @@ -1,2 +0,0 @@ -output "network" { value = "${ google_compute_network.cncf.self_link }" } -output "subnetwork" { value = "${ google_compute_subnetwork.cncf.self_link }" } From 9d018d6a52d05fece77d117236fa5c271dd15435 Mon Sep 17 00:00:00 2001 From: DLX Date: Mon, 1 May 2017 06:44:21 +1200 Subject: [PATCH 142/149] Output GKE Certs and Configure Kubectl --- provisioning/Dockerfile | 2 +- provisioning/gke/input.tf | 2 + provisioning/gke/modules.tf | 11 ++++ provisioning/gke/modules/cluster/output.tf | 7 +++ provisioning/gke/modules/kubeconfig/input.tf | 10 ++++ .../gke/modules/kubeconfig/kubeconfig.tf | 52 +++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 provisioning/gke/modules/cluster/output.tf create mode 100644 provisioning/gke/modules/kubeconfig/input.tf create mode 100644 provisioning/gke/modules/kubeconfig/kubeconfig.tf diff --git a/provisioning/Dockerfile b/provisioning/Dockerfile index 1c22ce9..94fae67 100644 --- a/provisioning/Dockerfile +++ b/provisioning/Dockerfile @@ -5,7 +5,7 @@ ENV GCLOUD_VERSION=150.0.0 ENV AWSCLI_VERSION=1.11.75 ENV AZURECLI_VERSION=2.0.2 ENV PACKETCLI_VERSION=1.33 -ENV TERRAFORM_VERSION=0.9.3 +ENV TERRAFORM_VERSION=0.9.4 ENV ARC=amd64 # Install AWS / AZURE CLI Deps diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf index 4354f00..282349a 100644 --- a/provisioning/gke/input.tf +++ b/provisioning/gke/input.tf @@ -10,3 +10,5 @@ variable "master_password" { default = "demo"} variable "vm_size" { default = "n1-standard-1"} variable "node_pool_count" { default = "3"} +output "kubeconfig" { value = "${ module.kubeconfig.kubeconfig }" } + diff --git a/provisioning/gke/modules.tf b/provisioning/gke/modules.tf index 695003f..98823c2 100644 --- a/provisioning/gke/modules.tf +++ b/provisioning/gke/modules.tf @@ -20,3 +20,14 @@ module "cluster" { vm_size = "${ var.vm_size }" node_pool_count = "${ var.node_pool_count }" } + +module "kubeconfig" { + source = "./modules/kubeconfig" + name = "${ var.name }" + project = "${ var.project }" + zone = "${ var.zone }" + endpoint = "${ module.cluster.endpoint }" + ca = "${ module.cluster.ca }" + admin = "${ module.cluster.admin }" + admin_key = "${ module.cluster.admin_key }" +} diff --git a/provisioning/gke/modules/cluster/output.tf b/provisioning/gke/modules/cluster/output.tf new file mode 100644 index 0000000..d147e99 --- /dev/null +++ b/provisioning/gke/modules/cluster/output.tf @@ -0,0 +1,7 @@ +output "endpoint" { value = "${ google_container_cluster.cncf.endpoint }" } + +output "ca" { value = "${ base64decode(google_container_cluster.cncf.master_auth.0.cluster_ca_certificate) }" } + +output "admin" { value = "${ base64decode(google_container_cluster.cncf.master_auth.0.client_certificate) }" } + +output "admin_key" { value = "${ base64decode(google_container_cluster.cncf.master_auth.0.client_key) }" } diff --git a/provisioning/gke/modules/kubeconfig/input.tf b/provisioning/gke/modules/kubeconfig/input.tf new file mode 100644 index 0000000..9c60410 --- /dev/null +++ b/provisioning/gke/modules/kubeconfig/input.tf @@ -0,0 +1,10 @@ +variable "name" {} +variable "project" {} +variable "zone" {} +variable "endpoint" {} +variable "ca" {} +variable "admin" {} +variable "admin_key" {} + + +output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/provisioning/gke/modules/kubeconfig/kubeconfig.tf b/provisioning/gke/modules/kubeconfig/kubeconfig.tf new file mode 100644 index 0000000..79f7f87 --- /dev/null +++ b/provisioning/gke/modules/kubeconfig/kubeconfig.tf @@ -0,0 +1,52 @@ + +resource "null_resource" "file" { + + provisioner "local-exec" { + command = < /cncf/ca.pem +echo "${ var.admin}" > /cncf/admin.pem +echo "${ var.admin_key}" > /cncf/admin_key.pem +LOCAL_EXEC + } +} + +data "template_file" "kubeconfig" { + template = < Date: Tue, 2 May 2017 08:56:45 +1200 Subject: [PATCH 143/149] Update Kubeconfig to Work with GKE --- provisioning/gke/gke.tf | 12 ++++++++++++ provisioning/gke/input.tf | 3 ++- provisioning/gke/modules.tf | 1 + provisioning/gke/modules/kubeconfig/input.tf | 1 + provisioning/gke/modules/kubeconfig/kubeconfig.tf | 11 ----------- 5 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 provisioning/gke/gke.tf diff --git a/provisioning/gke/gke.tf b/provisioning/gke/gke.tf new file mode 100644 index 0000000..9742826 --- /dev/null +++ b/provisioning/gke/gke.tf @@ -0,0 +1,12 @@ +provider "google" {} + +resource "null_resource" "file" { + + provisioner "local-exec" { + command = < /cncf/ca.pem +echo "${ var.admin}" > /cncf/admin.pem +echo "${ var.admin_key}" > /cncf/admin_key.pem +LOCAL_EXEC + } +} diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf index 282349a..20a4fef 100644 --- a/provisioning/gke/input.tf +++ b/provisioning/gke/input.tf @@ -4,11 +4,12 @@ variable "zone" { default = "us-central1-a" } variable "project" { default = "test-163823" } variable "cidr" { default = "10.0.0.0/16" } variable "node_count" { default = "3" } -variable "node_version" { default = "1.5.6" } +variable "node_version" { default = "1.6.2" } variable "master_user" { default = "cncf" } variable "master_password" { default = "demo"} variable "vm_size" { default = "n1-standard-1"} variable "node_pool_count" { default = "3"} +variable "data_dir" { default = "/cncf/data" } output "kubeconfig" { value = "${ module.kubeconfig.kubeconfig }" } diff --git a/provisioning/gke/modules.tf b/provisioning/gke/modules.tf index 98823c2..986c277 100644 --- a/provisioning/gke/modules.tf +++ b/provisioning/gke/modules.tf @@ -30,4 +30,5 @@ module "kubeconfig" { ca = "${ module.cluster.ca }" admin = "${ module.cluster.admin }" admin_key = "${ module.cluster.admin_key }" + data_dir = "${ var.data_dir }" } diff --git a/provisioning/gke/modules/kubeconfig/input.tf b/provisioning/gke/modules/kubeconfig/input.tf index 9c60410..4c1d3de 100644 --- a/provisioning/gke/modules/kubeconfig/input.tf +++ b/provisioning/gke/modules/kubeconfig/input.tf @@ -5,6 +5,7 @@ variable "endpoint" {} variable "ca" {} variable "admin" {} variable "admin_key" {} +variable "data_dir" {} output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/provisioning/gke/modules/kubeconfig/kubeconfig.tf b/provisioning/gke/modules/kubeconfig/kubeconfig.tf index 79f7f87..5dd3526 100644 --- a/provisioning/gke/modules/kubeconfig/kubeconfig.tf +++ b/provisioning/gke/modules/kubeconfig/kubeconfig.tf @@ -1,15 +1,4 @@ -resource "null_resource" "file" { - - provisioner "local-exec" { - command = < /cncf/ca.pem -echo "${ var.admin}" > /cncf/admin.pem -echo "${ var.admin_key}" > /cncf/admin_key.pem -LOCAL_EXEC - } -} - data "template_file" "kubeconfig" { template = < Date: Wed, 3 May 2017 08:19:55 +1200 Subject: [PATCH 144/149] Add Google Provider --- provisioning/gke/gke.tf | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/provisioning/gke/gke.tf b/provisioning/gke/gke.tf index 9742826..ab2565c 100644 --- a/provisioning/gke/gke.tf +++ b/provisioning/gke/gke.tf @@ -1,12 +1,2 @@ provider "google" {} -resource "null_resource" "file" { - - provisioner "local-exec" { - command = < /cncf/ca.pem -echo "${ var.admin}" > /cncf/admin.pem -echo "${ var.admin_key}" > /cncf/admin_key.pem -LOCAL_EXEC - } -} From 5f4aadad05544055210692501603f3c39a6fe712 Mon Sep 17 00:00:00 2001 From: DLX Date: Wed, 3 May 2017 08:20:28 +1200 Subject: [PATCH 145/149] Fix Syntax Error --- provisioning/provision.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/provision.sh b/provisioning/provision.sh index af2f279..0a81726 100755 --- a/provisioning/provision.sh +++ b/provisioning/provision.sh @@ -55,7 +55,7 @@ elif [ "$1" = "gce-deploy" ] ; then printf 'export KUBECONFIG=$(pwd)/data/${name}/kubeconfig \n\n'${NC} elif [ "$1" = "gce-destroy" ] ; then time terraform destroy -force ${DIR}/gce -eelif [ "$1" = "gke-deploy" ] ; then +elif [ "$1" = "gke-deploy" ] ; then terraform get ${DIR}/gke && \ time terraform apply ${DIR}/gke elif [ "$1" = "gke-destroy" ] ; then From 1a292e26ea4d1c9d26c14adb37b399f0b9d5e781 Mon Sep 17 00:00:00 2001 From: DLX Date: Wed, 3 May 2017 08:31:00 +1200 Subject: [PATCH 146/149] Use Top Level KUBECONFIG Module --- provisioning/gke/input.tf | 1 - provisioning/gke/modules.tf | 16 ++++---- provisioning/gke/modules/cluster/cluster.tf | 12 ++++++ provisioning/gke/modules/cluster/input.tf | 1 + provisioning/gke/modules/cluster/output.tf | 8 +--- provisioning/gke/modules/kubeconfig/input.tf | 11 ----- .../gke/modules/kubeconfig/kubeconfig.tf | 41 ------------------- 7 files changed, 22 insertions(+), 68 deletions(-) delete mode 100644 provisioning/gke/modules/kubeconfig/input.tf delete mode 100644 provisioning/gke/modules/kubeconfig/kubeconfig.tf diff --git a/provisioning/gke/input.tf b/provisioning/gke/input.tf index 20a4fef..b3d5121 100644 --- a/provisioning/gke/input.tf +++ b/provisioning/gke/input.tf @@ -12,4 +12,3 @@ variable "node_pool_count" { default = "3"} variable "data_dir" { default = "/cncf/data" } output "kubeconfig" { value = "${ module.kubeconfig.kubeconfig }" } - diff --git a/provisioning/gke/modules.tf b/provisioning/gke/modules.tf index 986c277..bd6639f 100644 --- a/provisioning/gke/modules.tf +++ b/provisioning/gke/modules.tf @@ -19,16 +19,16 @@ module "cluster" { master_password = "${ var.master_password }" vm_size = "${ var.vm_size }" node_pool_count = "${ var.node_pool_count }" + data_dir = "${ var.data_dir }" } module "kubeconfig" { - source = "./modules/kubeconfig" - name = "${ var.name }" - project = "${ var.project }" - zone = "${ var.zone }" - endpoint = "${ module.cluster.endpoint }" - ca = "${ module.cluster.ca }" - admin = "${ module.cluster.admin }" - admin_key = "${ module.cluster.admin_key }" + source = "../kubeconfig" + + ca_pem = "${ var.data_dir }/ca.pem" + admin_pem = "${ var.data_dir }/k8s-admin.pem" + admin_key_pem = "${ var.data_dir }/k8s-admin-key.pem" + fqdn_k8s = "${ module.cluster.fqdn_k8s }" data_dir = "${ var.data_dir }" + name = "gke_${ var.project }_${ var.zone }-a_${ var.name }" } diff --git a/provisioning/gke/modules/cluster/cluster.tf b/provisioning/gke/modules/cluster/cluster.tf index 8ca7363..f783c81 100644 --- a/provisioning/gke/modules/cluster/cluster.tf +++ b/provisioning/gke/modules/cluster/cluster.tf @@ -28,3 +28,15 @@ resource "google_container_cluster" "cncf" { ] } } + + +resource "null_resource" "file" { + + provisioner "local-exec" { + command = < "${ var.data_dir }/ca.pem" +echo "${ base64decode(google_container_cluster.cncf.master_auth.0.client_certificate) }" > "${ var.data_dir }/k8s-admin.pem" +echo "${ base64decode(google_container_cluster.cncf.master_auth.0.client_key) }" > "${ var.data_dir }/k8s-admin-key.pem" +LOCAL_EXEC + } +} diff --git a/provisioning/gke/modules/cluster/input.tf b/provisioning/gke/modules/cluster/input.tf index 7ab1f87..aa57ce7 100644 --- a/provisioning/gke/modules/cluster/input.tf +++ b/provisioning/gke/modules/cluster/input.tf @@ -10,4 +10,5 @@ variable "master_user" {} variable "master_password" {} variable "node_pool_count" {} variable "vm_size" {} +variable "data_dir" {} diff --git a/provisioning/gke/modules/cluster/output.tf b/provisioning/gke/modules/cluster/output.tf index d147e99..bf4a453 100644 --- a/provisioning/gke/modules/cluster/output.tf +++ b/provisioning/gke/modules/cluster/output.tf @@ -1,7 +1 @@ -output "endpoint" { value = "${ google_container_cluster.cncf.endpoint }" } - -output "ca" { value = "${ base64decode(google_container_cluster.cncf.master_auth.0.cluster_ca_certificate) }" } - -output "admin" { value = "${ base64decode(google_container_cluster.cncf.master_auth.0.client_certificate) }" } - -output "admin_key" { value = "${ base64decode(google_container_cluster.cncf.master_auth.0.client_key) }" } +output "fqdn_k8s" { value = "${ google_container_cluster.cncf.endpoint }" } diff --git a/provisioning/gke/modules/kubeconfig/input.tf b/provisioning/gke/modules/kubeconfig/input.tf deleted file mode 100644 index 4c1d3de..0000000 --- a/provisioning/gke/modules/kubeconfig/input.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "name" {} -variable "project" {} -variable "zone" {} -variable "endpoint" {} -variable "ca" {} -variable "admin" {} -variable "admin_key" {} -variable "data_dir" {} - - -output "kubeconfig" { value = "${ data.template_file.kubeconfig.rendered }" } diff --git a/provisioning/gke/modules/kubeconfig/kubeconfig.tf b/provisioning/gke/modules/kubeconfig/kubeconfig.tf deleted file mode 100644 index 5dd3526..0000000 --- a/provisioning/gke/modules/kubeconfig/kubeconfig.tf +++ /dev/null @@ -1,41 +0,0 @@ - -data "template_file" "kubeconfig" { - template = < Date: Thu, 4 May 2017 06:18:08 +1200 Subject: [PATCH 147/149] Add GKE to Cross-Cloud --- provisioning/cross-cloud/cloud.tf | 9 ++++++++- provisioning/gke/input.tf | 1 - provisioning/gke/output.tf | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 provisioning/gke/output.tf diff --git a/provisioning/cross-cloud/cloud.tf b/provisioning/cross-cloud/cloud.tf index 82547cf..4003bf8 100644 --- a/provisioning/cross-cloud/cloud.tf +++ b/provisioning/cross-cloud/cloud.tf @@ -25,6 +25,12 @@ module "gce" { data_dir = "${ var.data_dir }/gce" } +module "gke" { + source = "../gke" + name = "${ var.name }-gke" + data_dir = "${ var.data_dir}/gke" +} + resource "null_resource" "kubeconfig" { @@ -35,6 +41,7 @@ ${ module.aws.kubeconfig } ${ module.azure.kubeconfig } ${ module.packet.kubeconfig } ${ module.gce.kubeconfig } +${ module.gke.kubeconfig } LOCAL_EXEC } @@ -42,7 +49,7 @@ LOCAL_EXEC data "template_file" "kubeconfig" { template = < Date: Thu, 4 May 2017 06:20:41 +1200 Subject: [PATCH 148/149] Fix VAR for Packet Cross-Cloud --- provisioning/packet/modules/etcd/kube-apiserver.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/packet/modules/etcd/kube-apiserver.yml b/provisioning/packet/modules/etcd/kube-apiserver.yml index 4fff647..4bce964 100644 --- a/provisioning/packet/modules/etcd/kube-apiserver.yml +++ b/provisioning/packet/modules/etcd/kube-apiserver.yml @@ -26,7 +26,7 @@ spec: - --etcd-certfile=/etc/kubernetes/ssl/k8s-etcd.pem - --etcd-keyfile=/etc/kubernetes/ssl/k8s-etcd-key.pem - --runtime-config=extensions/v1beta1/networkpolicies=true,batch/v2alpha1 - - --etcd-servers=https://etcd.${ internal-tld }:2379 + - --etcd-servers=https://etcd.${ internal_tld }:2379 - --secure-port=443 - --service-account-lookup - --service-account-private-key-file=/etc/kubernetes/ssl/k8s-apiserver-key.pem From 0936c93af14c47e47d77f5ea98b5dc84294d618b Mon Sep 17 00:00:00 2001 From: DLX Date: Fri, 5 May 2017 05:48:53 +1200 Subject: [PATCH 149/149] Update .sh to Get Terraform before Destroy --- provisioning/aws/input.tf | 2 +- provisioning/provision.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/provisioning/aws/input.tf b/provisioning/aws/input.tf index 931bb95..0983dd9 100644 --- a/provisioning/aws/input.tf +++ b/provisioning/aws/input.tf @@ -32,5 +32,5 @@ variable "worker_node_max" { default = "5" } # Hyperkube # Set from https://quay.io/repository/coreos/hyperkube?tab=tags variable "kubelet_image_url" { default = "quay.io/coreos/hyperkube"} -variable "kubelet_image_tag" { default = "v1.6.1_coreos.0"} +variable "kubelet_image_tag" { default = "v1.5.1_coreos.0"} diff --git a/provisioning/provision.sh b/provisioning/provision.sh index 0a81726..96b2e03 100755 --- a/provisioning/provision.sh +++ b/provisioning/provision.sh @@ -57,7 +57,8 @@ elif [ "$1" = "gce-destroy" ] ; then time terraform destroy -force ${DIR}/gce elif [ "$1" = "gke-deploy" ] ; then terraform get ${DIR}/gke && \ - time terraform apply ${DIR}/gke + terraform apply -target module.vpc + time terraform apply ${DIR}/gke elif [ "$1" = "gke-destroy" ] ; then time terraform destroy -force ${DIR}/gke elif [ "$1" = "cross-cloud-deploy" ] ; then