From 135c9b29a7926484375e8633255681d2bc63644c Mon Sep 17 00:00:00 2001
From: moss2k13 <apps@mossakowski.ch>
Date: Tue, 11 Jan 2022 14:23:16 +0100
Subject: [PATCH] contrib: add cloud-init support for terraform vms (#8394)

* contrib: add cloud-init support for terraform vms

This change enables instance customization via cloud-init,
for example: additional CA certs, custom SSH access etc.

* contrib: update docs for terraform cloud-init

* contrib: disable yamllint in cloud-init

require-starting-space rule breaks cloud-init header

* contrib: documenation formatting

* yamllint: disable comments related checks

* docs: markdown formatting
---
 contrib/terraform/openstack/README.md         | 21 +++++++++++++++++++
 .../openstack/modules/compute/main.tf         | 12 +++++++++++
 .../modules/compute/templates/cloudinit.yaml  | 17 +++++++++++++++
 3 files changed, 50 insertions(+)
 create mode 100644 contrib/terraform/openstack/modules/compute/templates/cloudinit.yaml

diff --git a/contrib/terraform/openstack/README.md b/contrib/terraform/openstack/README.md
index d1abcfe76..2b7ca0ef0 100644
--- a/contrib/terraform/openstack/README.md
+++ b/contrib/terraform/openstack/README.md
@@ -416,6 +416,27 @@ terraform init ../../contrib/terraform/openstack
 
 This should finish fairly quickly telling you Terraform has successfully initialized and loaded necessary modules.
 
+### Customizing with cloud-init
+
+You can apply cloud-init based customization for the openstack instances before provisioning your cluster.
+One common template is used for all instances. Adjust the file shown below:
+`contrib/terraform/openstack/modules/compute/templates/cloudinit.yaml`
+For example, to enable openstack novnc access and ansible_user=root SSH access:
+
+```ShellSession
+#cloud-config
+## in some cases novnc console access is required
+## it requires ssh password to be set
+ssh_pwauth: yes
+chpasswd:
+  list: |
+    root:secret
+  expire: False
+
+## in some cases direct root ssh access via ssh key is required
+disable_root: false
+```
+
 ### Provisioning cluster
 
 You can apply the Terraform configuration to your cluster with the following command
diff --git a/contrib/terraform/openstack/modules/compute/main.tf b/contrib/terraform/openstack/modules/compute/main.tf
index fc118827e..5e0ba546a 100644
--- a/contrib/terraform/openstack/modules/compute/main.tf
+++ b/contrib/terraform/openstack/modules/compute/main.tf
@@ -15,6 +15,10 @@ data "openstack_images_image_v2" "image_master" {
   name = var.image_master == "" ? var.image : var.image_master
 }
 
+data "template_file" "cloudinit" {
+    template = file("${path.module}/templates/cloudinit.yaml")
+}
+
 resource "openstack_compute_keypair_v2" "k8s" {
   name       = "kubernetes-${var.cluster_name}"
   public_key = chomp(file(var.public_key_path))
@@ -175,6 +179,7 @@ resource "openstack_compute_instance_v2" "bastion" {
   image_id   = var.bastion_root_volume_size_in_gb == 0 ? local.image_to_use_node : null
   flavor_id  = var.flavor_bastion
   key_pair   = openstack_compute_keypair_v2.k8s.name
+  user_data  = data.template_file.cloudinit.rendered
 
   dynamic "block_device" {
     for_each = var.bastion_root_volume_size_in_gb > 0 ? [local.image_to_use_node] : []
@@ -215,6 +220,7 @@ resource "openstack_compute_instance_v2" "k8s_master" {
   image_id          = var.master_root_volume_size_in_gb == 0 ? local.image_to_use_master : null
   flavor_id         = var.flavor_k8s_master
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
 
   dynamic "block_device" {
@@ -262,6 +268,7 @@ resource "openstack_compute_instance_v2" "k8s_master_no_etcd" {
   image_id          = var.master_root_volume_size_in_gb == 0 ? local.image_to_use_master : null
   flavor_id         = var.flavor_k8s_master
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
 
   dynamic "block_device" {
@@ -309,6 +316,7 @@ resource "openstack_compute_instance_v2" "etcd" {
   image_id          = var.etcd_root_volume_size_in_gb == 0 ? local.image_to_use_master : null
   flavor_id         = var.flavor_etcd
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
   dynamic "block_device" {
     for_each = var.etcd_root_volume_size_in_gb > 0 ? [local.image_to_use_master] : []
@@ -392,6 +400,7 @@ resource "openstack_compute_instance_v2" "k8s_master_no_floating_ip_no_etcd" {
   image_id          = var.master_root_volume_size_in_gb == 0 ? local.image_to_use_master : null
   flavor_id         = var.flavor_k8s_master
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
   dynamic "block_device" {
     for_each = var.master_root_volume_size_in_gb > 0 ? [local.image_to_use_master] : []
@@ -434,6 +443,7 @@ resource "openstack_compute_instance_v2" "k8s_node" {
   image_id          = var.node_root_volume_size_in_gb == 0 ? local.image_to_use_node : null
   flavor_id         = var.flavor_k8s_node
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
   dynamic "block_device" {
     for_each = var.node_root_volume_size_in_gb > 0 ? [local.image_to_use_node] : []
@@ -480,6 +490,7 @@ resource "openstack_compute_instance_v2" "k8s_node_no_floating_ip" {
   image_id          = var.node_root_volume_size_in_gb == 0 ? local.image_to_use_node : null
   flavor_id         = var.flavor_k8s_node
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
   dynamic "block_device" {
     for_each = var.node_root_volume_size_in_gb > 0 ? [local.image_to_use_node] : []
@@ -522,6 +533,7 @@ resource "openstack_compute_instance_v2" "k8s_nodes" {
   image_id          = var.node_root_volume_size_in_gb == 0 ? local.image_to_use_node : null
   flavor_id         = each.value.flavor
   key_pair          = openstack_compute_keypair_v2.k8s.name
+  user_data         = data.template_file.cloudinit.rendered
 
   dynamic "block_device" {
     for_each = var.node_root_volume_size_in_gb > 0 ? [local.image_to_use_node] : []
diff --git a/contrib/terraform/openstack/modules/compute/templates/cloudinit.yaml b/contrib/terraform/openstack/modules/compute/templates/cloudinit.yaml
new file mode 100644
index 000000000..396acb9f7
--- /dev/null
+++ b/contrib/terraform/openstack/modules/compute/templates/cloudinit.yaml
@@ -0,0 +1,17 @@
+# yamllint disable rule:comments
+#cloud-config
+## in some cases novnc console access is required
+## it requires ssh password to be set
+#ssh_pwauth: yes
+#chpasswd:
+#  list: |
+#    root:secret
+#  expire: False
+
+## in some cases direct root ssh access via ssh key is required
+#disable_root: false
+
+## in some cases additional CA certs are required
+#ca-certs:
+#  trusted: |
+#      -----BEGIN CERTIFICATE-----
-- 
GitLab