Skip to content

Commit 09d90ee

Browse files
committed
Manage optional features
1 parent 07bee76 commit 09d90ee

File tree

6 files changed

+171
-39
lines changed

6 files changed

+171
-39
lines changed

README.md

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
1-
# Scaleway cloud-init demo using Terraform
1+
# Scaleway development instances using Terraform and cloud-init
22

3-
## Usage
3+
## Foreword
4+
5+
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).
6+
7+
## Prerequisites
8+
9+
* Terraform 1.0+
10+
* [jq](https://stedolan.github.io/jq/)
11+
* 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)
12+
13+
## Introduction
414

5-
1. Create your SSH key pair and [add it in your authorized keys](https://console.scaleway.com/account/credentials)
15+
1. Define shell helper function to set Terraform variables
16+
17+
```
18+
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 }
19+
```
20+
21+
> 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)
22+
23+
2. Create your SSH key pair and [add it in your authorized keys](https://console.scaleway.com/project/credentials)
624

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

11-
2. [Retrieve your project credentials](https://console.scaleway.com/project/credentials) and export them:
29+
> Add the SSH key file in Terraform variables
30+
> ```
31+
> set_tfvar ssh_key_file '"~/.ssh/scaleway"'
32+
> ```
33+
34+
3. [Retrieve your project credentials](https://console.scaleway.com/project/credentials) and export them:
1235
1336
```
1437
export SCW_DEFAULT_PROJECT_ID=<REDACTED>
@@ -18,17 +41,56 @@ export SCW_DEFAULT_REGION=fr-par
1841
export SCW_DEFAULT_ZONE=fr-par-1
1942
```
2043
21-
3. Initialize Terraform
44+
## Usage
45+
46+
1. Initialize Terraform
47+
2248
```
2349
terraform init
2450
```
2551
52+
2. Define username
53+
54+
```
55+
set_tfvar username '"developer"'
56+
```
57+
58+
> Mind the double quotes
59+
60+
3. Select optional features
61+
62+
* [Oh My ZSH](https://ohmyz.sh/):
63+
```
64+
set_tfvar feature_omz true
65+
```
66+
67+
* [Docker](https://www.docker.com/):
68+
```
69+
set_tfvar feature_docker true
70+
```
71+
72+
* [nvm](https://github.com/nvm-sh/nvm) (for Node developments):
73+
```
74+
set_tfvar feature_nvm true
75+
```
76+
77+
* [SDKMAN!](https://sdkman.io/) (for Java developments):
78+
```
79+
set_tfvar feature_sdkman true
80+
```
81+
2682
4. Apply default plan
2783
```
2884
terraform apply
2985
```
3086
3187
5. SSH to the (first) created host
88+
3289
```
33-
ssh -i ~/.ssh/scaleway root@$(terraform output --json | jq -r '.public_ips.value[0]')
90+
eval `terraform output --json ssh_commands | jq -r ".[0]"`
3491
```
92+
93+
> To display the SSH command used to connect:
94+
> ```
95+
> echo $(terraform output --json ssh_commands | jq -r ".[0]")
96+
> ```

cloud-init-user-data

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
# users
44
users:
5-
- default
65
- name: ${user}
76
sudo: ALL=(ALL) NOPASSWD:ALL
87
shell: /bin/zsh
@@ -14,15 +13,19 @@ package_upgrade: true
1413

1514
packages:
1615
- curl
16+
- jq
1717
- git
1818
- sudo
19+
- unzip
20+
- zip
1921
- zsh
2022

2123
runcmd:
2224
# copy authorized SSH keys for the created user
2325
- 'mkdir -p /home/${user}/.ssh'
2426
- 'cp /root/.ssh/authorized_keys /home/${user}/.ssh/authorized_keys'
2527
- 'chown ${user}:root /home/${user}/.ssh/authorized_keys'
28+
%{if feature_omz ~}
2629
# install Oh My ZSH for root
2730
- 'git clone --depth=1 https://github.com/robbyrussell/oh-my-zsh.git /root/.oh-my-zsh'
2831
- 'cp /root/.oh-my-zsh/templates/zshrc.zsh-template /root/.zshrc'
@@ -32,15 +35,35 @@ runcmd:
3235
- 'runuser -l ${user} -c "git clone --depth=1 https://github.com/robbyrussell/oh-my-zsh.git /home/${user}/.oh-my-zsh"'
3336
- 'runuser -l ${user} -c "cp /home/${user}/.oh-my-zsh/templates/zshrc.zsh-template /home/${user}/.zshrc"'
3437
- 'sed -i "s/ZSH_THEME=\".*\"/ZSH_THEME=\"ys\"/" /home/${user}/.zshrc'
38+
%{endif~}
39+
%{if feature_docker ~}
3540
# install Docker
36-
- 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/${distrib} $(lsb_release -cs) stable"'
3741
- 'apt-get update'
38-
- 'apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common'
39-
- 'curl -fsSL https://download.docker.com/linux/${distrib}/gpg > /tmp/docker.gpg'
40-
- 'apt-key add /tmp/docker.gpg'
42+
- 'apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release'
43+
- 'curl -fsSL https://download.docker.com/linux/${distrib}/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg'
44+
- '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'
4145
- 'apt-get update'
42-
- 'apt-get install -y docker-ce'
43-
46+
- 'apt-get install -y docker-ce docker-ce-cli containerd.io'
47+
%{endif~}
48+
%{if feature_nvm ~}
49+
# install NVM
50+
- '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"'
51+
- 'runuser -l ${user} -c "touch /home/${user}/.zshrc"'
52+
- |
53+
cat << 'EOF' >> /home/${user}/.zshrc
54+
export NVM_DIR="$([ -z "$${XDG_CONFIG_HOME-}" ] && printf %s "$${HOME}/.nvm" || printf %s "$${XDG_CONFIG_HOME}/nvm")"
55+
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
56+
EOF
57+
%{endif~}
58+
%{if feature_sdkman ~}
59+
# install SDKMAN!
60+
- 'runuser -l ${user} -c "curl -s "https://get.sdkman.io" | bash"'
61+
- 'runuser -l ${user} -c "touch /home/${user}/.zshrc"'
62+
- |
63+
cat << 'EOF' >> /home/${user}/.zshrc
64+
source "$HOME/.sdkman/bin/sdkman-init.sh"
65+
EOF
66+
%{endif~}
4467
#
4568
power_state:
4669
delay: "+1"

main.tf

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ data "template_file" "userdata" {
66
template = file("${path.module}/cloud-init-user-data")
77

88
vars = {
9-
distrib = var.docker_distrib
10-
user = var.username
9+
distrib = regex("^ubuntu|debian", var.server_image)
10+
user = var.username
11+
feature_docker = var.feature_docker
12+
feature_nvm = var.feature_nvm
13+
feature_omz = var.feature_omz
14+
feature_sdkman = var.feature_sdkman
1115
}
1216
}
1317

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

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

19-
image = var.server_image
20-
type = var.server_type
21-
enable_dynamic_ip = true
23+
image = var.server_image
24+
type = var.server_type
25+
enable_dynamic_ip = true
2226

2327
# initialization sequence
2428
cloud_init = data.template_file.userdata.rendered
29+
}
2530

26-
connection {
27-
host = element(scaleway_instance_server.node.*.public_ip, count.index)
28-
user = var.username
29-
private_key = file(var.ssh_key_file)
30-
}
31+
resource "null_resource" "wait_for_init" {
32+
count = var.node_count
3133

3234
provisioner "remote-exec" {
35+
connection {
36+
host = scaleway_instance_server.node[count.index].public_ip
37+
user = "root"
38+
private_key = file(var.ssh_key_file)
39+
}
40+
3341
inline = [
42+
"touch /var/log/cloud-init-output.log",
3443
"tail -f /var/log/cloud-init-output.log &",
3544
"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do sleep 10; done;",
3645
]
3746
}
47+
3848
provisioner "local-exec" {
3949
command = "sleep 80" # wait more than 1 minute for the instance to be rebooted
4050
}
4151
}
4252

43-
resource "null_resource" "node" {
44-
count = var.node_count
45-
46-
connection {
47-
host = element(scaleway_instance_server.node.*.public_ip, count.index)
48-
user = var.username
49-
private_key = file(var.ssh_key_file)
50-
}
53+
resource "null_resource" "docker_test" {
54+
count = var.feature_docker && var.feature_docker_test ? var.node_count : 0
5155

5256
provisioner "remote-exec" {
57+
connection {
58+
host = element(scaleway_instance_server.node.*.public_ip, count.index)
59+
user = var.username
60+
private_key = file(var.ssh_key_file)
61+
}
62+
5363
inline = [
5464
"mkdir -p ~/www",
5565
"echo 'It works' > ~/www/index.html",
5666
"docker run --name http-nginx --restart=always -v ~/www:/usr/share/nginx/html:ro -p 80:80 -d nginx",
5767
]
5868
}
5969
}
60-

outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ output "public_ips" {
22
value = scaleway_instance_server.node.*.public_ip
33
}
44

5+
output "ssh_commands" {
6+
value = [
7+
for ip in scaleway_instance_server.node.*.public_ip : "ssh -i ${var.ssh_key_file} ${var.username}@${ip}"
8+
]
9+
}

variables.tf

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,57 @@ variable "node_name" {
1111
variable "server_image" {
1212
type = string
1313
default = "ubuntu_focal"
14+
validation {
15+
condition = can(regex("^(debian_|ubuntu_).*$", var.server_image))
16+
error_message = "The server_image value must start with ubuntu_ or debian_ (ubuntu_focal, debian_buster, ...)."
17+
}
1418
}
1519

1620
variable "server_type" {
1721
type = string
1822
default = "DEV1-S"
23+
validation {
24+
condition = can(regex("^(DEV1-(S|M|L|XL))|(GP1-(XS|S|M|L|XL))$", var.server_type))
25+
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)."
26+
}
1927
}
2028

2129
variable "ssh_key_file" {
22-
type = string
23-
default = "~/.ssh/scaleway"
30+
type = string
2431
}
2532

2633
variable "username" {
2734
type = string
2835
default = "user"
2936
}
3037

31-
variable "docker_distrib" {
32-
type = string
33-
default = "ubuntu"
38+
# optional features
39+
variable "feature_omz" {
40+
type = bool
41+
description = "Whether to install Oh My ZSH or not"
42+
default = false
43+
}
44+
45+
variable "feature_docker" {
46+
type = bool
47+
description = "Whether to install Docker or not"
48+
default = false
49+
}
50+
51+
variable "feature_docker_test" {
52+
type = bool
53+
description = "Whether to deploy a Docker test container when Docker feature is enabled (otherwise it will be ignored)"
54+
default = true
55+
}
56+
57+
variable "feature_nvm" {
58+
type = bool
59+
description = "Whether to install NVM or not"
60+
default = false
61+
}
62+
63+
variable "feature_sdkman" {
64+
type = bool
65+
description = "Whether to install SDKMAN! or not"
66+
default = false
3467
}

versions.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
terraform {
22
required_providers {
33
scaleway = {
4-
source = "scaleway/scaleway"
4+
source = "scaleway/scaleway"
55
version = "~> 2.0"
66
}
77
}
8-
required_version = ">= 0.13"
8+
required_version = ">= 1.0"
99
}

0 commit comments

Comments
 (0)