From 5f62aa1360cdc96868367cc709f6fade88ea6dbb Mon Sep 17 00:00:00 2001
From: "Marco Emilio \"sphakka\" Poleggi" <marcoep@ieee.org>
Date: Tue, 12 Nov 2024 10:30:57 +0100
Subject: [PATCH] Fixed cloud-init boilerplate. More TF cloud-init data
 resource info

Signed-off-by: Marco Emilio "sphakka" Poleggi <marcoep@ieee.org>
---
 SwitchEngines/README.md                    | 78 ++++++++++++++--------
 SwitchEngines/conf/cloud-init.add-ssh.yaml |  5 +-
 2 files changed, 52 insertions(+), 31 deletions(-)

diff --git a/SwitchEngines/README.md b/SwitchEngines/README.md
index 00d195c..480fb6b 100644
--- a/SwitchEngines/README.md
+++ b/SwitchEngines/README.md
@@ -548,22 +548,13 @@ you shall:
       lcl$ mkdir -p keys
       lcl$ yes | ssh-keygen -t ed25519 -f keys/tf-cloud-init -C terraform@TF-lab
       ```
-  1. Create a new cloud-init file
-     `conf/cloud-init.add-ssh.yaml` with the following content:
+  1. Open the boilerplate file `conf/cloud-init.add-ssh.yaml` and add the
+     content of your public SSH key created above:
      ``` yaml
-     #cloud-config
-     #^^^^^^^^^^^^
-     # DO NOT TOUCH the first line!
-     ---
-     groups:
-       - terraform
-
+     ...
      users:
-       - default
        - name: terraform
-         gecos: terraform
-         primary_group: terraform
-         groups: [users, admin]
+         ...
          ssh_authorized_keys:
            - <your-SSH-pub-key-on-one-line>
      ```
@@ -608,12 +599,17 @@ Now you can provision an instance and have SSH access to it. Let's write a plan
 
 You shall:
 
-1. Declare a variable or [_local_](https://developer.hashicorp.com/terraform/language/values/locals) `insance_count`.
-1. Extend your "app_server" resource with 
-  - a [`count` argument](https://developer.hashicorp.com/terraform/language/meta-arguments/count) using `insance_count`,
+1. Declare a variable or
+   [_local_](https://developer.hashicorp.com/terraform/language/values/locals)
+   `instance_count`.
+1. Extend your "app_server" resource with
+  - a [`count`
+    argument](https://developer.hashicorp.com/terraform/language/meta-arguments/count)
+    using `instance_count`,
   - a `name` argument showing the instance count index.
-1. Extend the networking-related resources with the count argument plus the index where necessary.
-1. Modify your `outputs` to show arrays instead of scalar values. 
+1. Extend the networking-related resources with the count argument plus the
+   index where necessary.
+1. Modify your `outputs` to show arrays instead of scalar values.
 
 Once done and your cluster provisioned, the following command should yield a single element of an output array:
 ```shell
@@ -718,32 +714,54 @@ Enter the [`cloudinit-config` data source](https://registry.terraform.io/provide
 [Data
 sources](https://developer.hashicorp.com/terraform/language/data-sources)
 provide special blocks to store information that can be used by resource
-blocks. You already used a data block for the FIP port association.
+blocks. :bulb: You already used a data block for the FIP port association.
 
 The cloudinit-config data source supports the declaration of several parts
 which are *merged* together into a single Multi-part MIME piece, so that the
-result can be ingested by the instance resource's `user_data` attribute.
+result can be ingested by the instance resource's `user_data`
+attribute. E.g., with two files:
+``` yaml
+data "cloudinit_config" "my_config" {
+  # for debugging
+  gzip          = false
+  base64_encode = false
+
+  part {
+    filename     = "file-1"
+    content_type = "text/cloud-config"
+    content      = file("file-1.yml")
+	merge_type   = "list(append)+dict(no_replace,recurse_list)"
+  }
 
-You shall:
+  part {
+    filename     = "file-2"
+    # ...
+  }
+}
+```
+
+:bulb: N.B.:
+  * The `merge_type` attribute might be needed to correctly merge lists and
+    dictionaries. Without that, a list or dict with the same (key) name in the
+    two files would be overwritten! See also [the official Cloud-init
+    reference](https://cloudinit.readthedocs.io/en/latest/reference/merging.html).
+  * The *merged* result of the whole block is provided by the read-only
+    attribute: `data.cloudinit_config.my_config.rendered`.
+
+::hammer_and_wrench:: **Over to you now.** You shall:
 
 1. Split your cloud-init YAML file above into 2 files:
    - `conf/cloud-init.users.yaml` with basic user-related configuration, and
    - `conf/cloud-init.packages.yaml` with package-related configuration.
 1. Modify your `main.tf`:
    1. Add a cloudinit-config data block with two parts declaring each one of
-      the above YAML conf file.
+      the above YAML conf file. Mind to use the correct `merge_type`.
    1. Change, in the `app_server` resource block, the `user_data` assignment to
       get the "rendered" output of the cloudinit-config data block.
 
 Then, rerun terraform apply, connect to your instance and verify that
 everything is there.
 
-:question: What if one needs to modify or override in the second cloud-init
-YAML file something already declared in the first one? E.g., adding a special
-package-provided group to the default user. There's risk of breaking things,
-unless the correct [user-data merge strategy is
-specified](https://cloudinit.readthedocs.io/en/latest/reference/merging.html).
-
 
 ### Task #9: Provisioning KinD with Cloud-init ###
 
@@ -811,7 +829,7 @@ provisioning API. Terraform provides also special resources, not bound to any
 provider (no backend API), which can be used to encapsulate provisioners for
 local or remote operations.
 
-In our case, the workflow, implemented within the same TF plan, is the the
+In our case, the workflow, implemented within the same TF plan, is the
 following:
 
 1. Provision an instance with the OpenStack provider.
@@ -886,6 +904,7 @@ can test the creation of the two resources separately.
   bit (at least 20-30 seconds) after each `kind/kubectl` command.
 
 
+<!--
 #### Task #10.2 Auxiliary K8s plan provisioning  ####
 
 :warning: **Under construction!**
@@ -897,3 +916,4 @@ provider](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/do
 and exploit an [SSH tunnel to convey K8s API
 calls](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-k8s/-/blob/main/README.md#controlling-your-cluster-from-a-remote-console)
 to the remote KinD instance.
+-->
diff --git a/SwitchEngines/conf/cloud-init.add-ssh.yaml b/SwitchEngines/conf/cloud-init.add-ssh.yaml
index 88c19b1..ef74062 100644
--- a/SwitchEngines/conf/cloud-init.add-ssh.yaml
+++ b/SwitchEngines/conf/cloud-init.add-ssh.yaml
@@ -3,7 +3,6 @@
 # DO NOT TOUCH the first line!
 ---
 groups:
-  - debian: [root, sys]
   - terraform
 
 users:
@@ -11,6 +10,8 @@ users:
   - name: terraform
     gecos: terraform
     primary_group: terraform
-    groups: users, admin
+    groups: [users, admin]
+    # permit running any commands as root
+    sudo: ALL=(ALL) NOPASSWD:ALL
     ssh_authorized_keys:
       - <your-SSH-pub-key-on-one-line>
-- 
GitLab