Skip to content

Commit

Permalink
Manage optional features
Browse files Browse the repository at this point in the history
  • Loading branch information
debovema committed Aug 4, 2021
1 parent 07bee76 commit 09d90ee
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 39 deletions.
74 changes: 68 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
# Scaleway cloud-init demo using Terraform
# Scaleway development instances using Terraform and cloud-init

## Usage
## Foreword

This Terraform module can be used to create cheap and disposable development machines with Scaleway cloud provider. For instance, these machines are well suited to be used with [Visual Studio Code SSH remote developement feature](https://code.visualstudio.com/docs/remote/ssh).

## Prerequisites

* Terraform 1.0+
* [jq](https://stedolan.github.io/jq/)
* A [Scaleway project](https://console.scaleway.com/project/) (default project is fine but [creating a new one](https://www.scaleway.com/en/docs/scaleway-project/) is encouraged)

## Introduction

1. Create your SSH key pair and [add it in your authorized keys](https://console.scaleway.com/account/credentials)
1. Define shell helper function to set Terraform variables

```
set_tfvar() { ( [ $(grep "$1" terraform.tfvars 2>/dev/null | wc -l) -gt 0 ] && sed -i -e "/^$1 /s/=.*$/= $(echo $2 | sed 's|\"|\\"|g' | sed 's|/|\\/|g')/" terraform.tfvars ) || echo "$1 = $2" >> terraform.tfvars }
```

> This shell function takes two arguments (the variable key and value) and put the *key = value* in the *terraform.tfvars* file regardless of the existence of the variable (create or replace behaviour)
2. Create your SSH key pair and [add it in your authorized keys](https://console.scaleway.com/project/credentials)

```
ssh-keygen -t rsa -b 4096 -q -C 'scaleway' -N '' -f ~/.ssh/scaleway
```

2. [Retrieve your project credentials](https://console.scaleway.com/project/credentials) and export them:
> Add the SSH key file in Terraform variables
> ```
> set_tfvar ssh_key_file '"~/.ssh/scaleway"'
> ```
3. [Retrieve your project credentials](https://console.scaleway.com/project/credentials) and export them:

```
export SCW_DEFAULT_PROJECT_ID=<REDACTED>
Expand All @@ -18,17 +41,56 @@ export SCW_DEFAULT_REGION=fr-par
export SCW_DEFAULT_ZONE=fr-par-1
```

3. Initialize Terraform
## Usage

1. Initialize Terraform

```
terraform init
```

2. Define username

```
set_tfvar username '"developer"'
```

> Mind the double quotes
3. Select optional features

* [Oh My ZSH](https://ohmyz.sh/):
```
set_tfvar feature_omz true
```

* [Docker](https://www.docker.com/):
```
set_tfvar feature_docker true
```

* [nvm](https://github.com/nvm-sh/nvm) (for Node developments):
```
set_tfvar feature_nvm true
```

* [SDKMAN!](https://sdkman.io/) (for Java developments):
```
set_tfvar feature_sdkman true
```

4. Apply default plan
```
terraform apply
```

5. SSH to the (first) created host

```
ssh -i ~/.ssh/scaleway root@$(terraform output --json | jq -r '.public_ips.value[0]')
eval `terraform output --json ssh_commands | jq -r ".[0]"`
```

> To display the SSH command used to connect:
> ```
> echo $(terraform output --json ssh_commands | jq -r ".[0]")
> ```
37 changes: 30 additions & 7 deletions cloud-init-user-data
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

# users
users:
- default
- name: ${user}
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/zsh
Expand All @@ -14,15 +13,19 @@ package_upgrade: true

packages:
- curl
- jq
- git
- sudo
- unzip
- zip
- zsh

runcmd:
# copy authorized SSH keys for the created user
- 'mkdir -p /home/${user}/.ssh'
- 'cp /root/.ssh/authorized_keys /home/${user}/.ssh/authorized_keys'
- 'chown ${user}:root /home/${user}/.ssh/authorized_keys'
%{if feature_omz ~}
# install Oh My ZSH for root
- 'git clone --depth=1 https://github.com/robbyrussell/oh-my-zsh.git /root/.oh-my-zsh'
- 'cp /root/.oh-my-zsh/templates/zshrc.zsh-template /root/.zshrc'
Expand All @@ -32,15 +35,35 @@ runcmd:
- 'runuser -l ${user} -c "git clone --depth=1 https://github.com/robbyrussell/oh-my-zsh.git /home/${user}/.oh-my-zsh"'
- 'runuser -l ${user} -c "cp /home/${user}/.oh-my-zsh/templates/zshrc.zsh-template /home/${user}/.zshrc"'
- 'sed -i "s/ZSH_THEME=\".*\"/ZSH_THEME=\"ys\"/" /home/${user}/.zshrc'
%{endif~}
%{if feature_docker ~}
# install Docker
- 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/${distrib} $(lsb_release -cs) stable"'
- 'apt-get update'
- 'apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common'
- 'curl -fsSL https://download.docker.com/linux/${distrib}/gpg > /tmp/docker.gpg'
- 'apt-key add /tmp/docker.gpg'
- 'apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release'
- 'curl -fsSL https://download.docker.com/linux/${distrib}/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg'
- 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${distrib} $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null'
- 'apt-get update'
- 'apt-get install -y docker-ce'

- 'apt-get install -y docker-ce docker-ce-cli containerd.io'
%{endif~}
%{if feature_nvm ~}
# install NVM
- 'runuser -l ${user} -c "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r .tag_name)/install.sh | bash"'
- 'runuser -l ${user} -c "touch /home/${user}/.zshrc"'
- |
cat << 'EOF' >> /home/${user}/.zshrc
export NVM_DIR="$([ -z "$${XDG_CONFIG_HOME-}" ] && printf %s "$${HOME}/.nvm" || printf %s "$${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
EOF
%{endif~}
%{if feature_sdkman ~}
# install SDKMAN!
- 'runuser -l ${user} -c "curl -s "https://get.sdkman.io" | bash"'
- 'runuser -l ${user} -c "touch /home/${user}/.zshrc"'
- |
cat << 'EOF' >> /home/${user}/.zshrc
source "$HOME/.sdkman/bin/sdkman-init.sh"
EOF
%{endif~}
#
power_state:
delay: "+1"
Expand Down
47 changes: 28 additions & 19 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ data "template_file" "userdata" {
template = file("${path.module}/cloud-init-user-data")

vars = {
distrib = var.docker_distrib
user = var.username
distrib = regex("^ubuntu|debian", var.server_image)
user = var.username
feature_docker = var.feature_docker
feature_nvm = var.feature_nvm
feature_omz = var.feature_omz
feature_sdkman = var.feature_sdkman
}
}

Expand All @@ -16,45 +20,50 @@ resource "scaleway_instance_server" "node" {

name = "${var.node_name}-${count.index}"

image = var.server_image
type = var.server_type
enable_dynamic_ip = true
image = var.server_image
type = var.server_type
enable_dynamic_ip = true

# initialization sequence
cloud_init = data.template_file.userdata.rendered
}

connection {
host = element(scaleway_instance_server.node.*.public_ip, count.index)
user = var.username
private_key = file(var.ssh_key_file)
}
resource "null_resource" "wait_for_init" {
count = var.node_count

provisioner "remote-exec" {
connection {
host = scaleway_instance_server.node[count.index].public_ip
user = "root"
private_key = file(var.ssh_key_file)
}

inline = [
"touch /var/log/cloud-init-output.log",
"tail -f /var/log/cloud-init-output.log &",
"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do sleep 10; done;",
]
}

provisioner "local-exec" {
command = "sleep 80" # wait more than 1 minute for the instance to be rebooted
}
}

resource "null_resource" "node" {
count = var.node_count

connection {
host = element(scaleway_instance_server.node.*.public_ip, count.index)
user = var.username
private_key = file(var.ssh_key_file)
}
resource "null_resource" "docker_test" {
count = var.feature_docker && var.feature_docker_test ? var.node_count : 0

provisioner "remote-exec" {
connection {
host = element(scaleway_instance_server.node.*.public_ip, count.index)
user = var.username
private_key = file(var.ssh_key_file)
}

inline = [
"mkdir -p ~/www",
"echo 'It works' > ~/www/index.html",
"docker run --name http-nginx --restart=always -v ~/www:/usr/share/nginx/html:ro -p 80:80 -d nginx",
]
}
}

5 changes: 5 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ output "public_ips" {
value = scaleway_instance_server.node.*.public_ip
}

output "ssh_commands" {
value = [
for ip in scaleway_instance_server.node.*.public_ip : "ssh -i ${var.ssh_key_file} ${var.username}@${ip}"
]
}
43 changes: 38 additions & 5 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,57 @@ variable "node_name" {
variable "server_image" {
type = string
default = "ubuntu_focal"
validation {
condition = can(regex("^(debian_|ubuntu_).*$", var.server_image))
error_message = "The server_image value must start with ubuntu_ or debian_ (ubuntu_focal, debian_buster, ...)."
}
}

variable "server_type" {
type = string
default = "DEV1-S"
validation {
condition = can(regex("^(DEV1-(S|M|L|XL))|(GP1-(XS|S|M|L|XL))$", var.server_type))
error_message = "The server_type value must be a valid Scaleway instance type (DEV1-S, DEV1-M, DEV1-L, DEV1-XL, GP1-XS, GP1-S, GP1-M, GP1-L, GP1-XL)."
}
}

variable "ssh_key_file" {
type = string
default = "~/.ssh/scaleway"
type = string
}

variable "username" {
type = string
default = "user"
}

variable "docker_distrib" {
type = string
default = "ubuntu"
# optional features
variable "feature_omz" {
type = bool
description = "Whether to install Oh My ZSH or not"
default = false
}

variable "feature_docker" {
type = bool
description = "Whether to install Docker or not"
default = false
}

variable "feature_docker_test" {
type = bool
description = "Whether to deploy a Docker test container when Docker feature is enabled (otherwise it will be ignored)"
default = true
}

variable "feature_nvm" {
type = bool
description = "Whether to install NVM or not"
default = false
}

variable "feature_sdkman" {
type = bool
description = "Whether to install SDKMAN! or not"
default = false
}
4 changes: 2 additions & 2 deletions versions.tf
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
terraform {
required_providers {
scaleway = {
source = "scaleway/scaleway"
source = "scaleway/scaleway"
version = "~> 2.0"
}
}
required_version = ">= 0.13"
required_version = ">= 1.0"
}

0 comments on commit 09d90ee

Please sign in to comment.