From 64dbf2e429e037b968b90612fc062779a39d6b11 Mon Sep 17 00:00:00 2001
From: Cristian Calin <6627509+cristicalin@users.noreply.github.com>
Date: Fri, 27 Jan 2023 07:24:25 +0200
Subject: [PATCH] update equinox terraform code to fix kubespray CI (#9702)

* add terraform lock files to ignore list

* move contrib/terraform/metal to contrib/terraform/equinix to reflect upstream change
---
 .gitignore                                    |  1 +
 .gitlab-ci/terraform.yml                      |  4 +--
 .../terraform/{metal => equinix}/README.md    | 23 ++++++++------
 contrib/terraform/{metal => equinix}/hosts    |  0
 .../terraform/{metal => equinix}/kubespray.tf | 31 ++++++++-----------
 contrib/terraform/equinix/output.tf           | 16 ++++++++++
 contrib/terraform/equinix/provider.tf         | 13 ++++++++
 .../sample-inventory/cluster.tfvars           |  5 ++-
 .../sample-inventory/group_vars               |  0
 .../terraform/{metal => equinix}/variables.tf |  4 +--
 contrib/terraform/metal/output.tf             | 16 ----------
 contrib/terraform/metal/versions.tf           |  9 ------
 contrib/terraform/terraform.py                | 29 ++++++++++++-----
 13 files changed, 85 insertions(+), 66 deletions(-)
 rename contrib/terraform/{metal => equinix}/README.md (90%)
 rename contrib/terraform/{metal => equinix}/hosts (100%)
 rename contrib/terraform/{metal => equinix}/kubespray.tf (72%)
 create mode 100644 contrib/terraform/equinix/output.tf
 create mode 100644 contrib/terraform/equinix/provider.tf
 rename contrib/terraform/{metal => equinix}/sample-inventory/cluster.tfvars (88%)
 rename contrib/terraform/{metal => equinix}/sample-inventory/group_vars (100%)
 rename contrib/terraform/{metal => equinix}/variables.tf (92%)
 delete mode 100644 contrib/terraform/metal/output.tf
 delete mode 100644 contrib/terraform/metal/versions.tf

diff --git a/.gitignore b/.gitignore
index 43bf3d934..4f576a99f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ contrib/offline/offline-files.tar.gz
 *.bak
 *.tfstate
 *.tfstate.backup
+*.lock.hcl
 .terraform/
 contrib/terraform/aws/credentials.tfvars
 .terraform.lock.hcl
diff --git a/.gitlab-ci/terraform.yml b/.gitlab-ci/terraform.yml
index 8ffb11163..055984e82 100644
--- a/.gitlab-ci/terraform.yml
+++ b/.gitlab-ci/terraform.yml
@@ -60,11 +60,11 @@ tf-validate-openstack:
     PROVIDER: openstack
     CLUSTER: $CI_COMMIT_REF_NAME
 
-tf-validate-metal:
+tf-validate-equinix:
   extends: .terraform_validate
   variables:
     TF_VERSION: $TERRAFORM_VERSION
-    PROVIDER: metal
+    PROVIDER: equinix
     CLUSTER: $CI_COMMIT_REF_NAME
 
 tf-validate-aws:
diff --git a/contrib/terraform/metal/README.md b/contrib/terraform/equinix/README.md
similarity index 90%
rename from contrib/terraform/metal/README.md
rename to contrib/terraform/equinix/README.md
index e49e6c97b..d1eb71f3f 100644
--- a/contrib/terraform/metal/README.md
+++ b/contrib/terraform/equinix/README.md
@@ -12,7 +12,7 @@ This will install a Kubernetes cluster on Equinix Metal. It should work in all l
 The terraform configuration inspects variables found in
 [variables.tf](variables.tf) to create resources in your Equinix Metal project.
 There is a [python script](../terraform.py) that reads the generated`.tfstate`
-file to generate a dynamic inventory that is consumed by [cluster.yml](../../..//cluster.yml)
+file to generate a dynamic inventory that is consumed by [cluster.yml](../../../cluster.yml)
 to actually install Kubernetes with Kubespray.
 
 ### Kubernetes Nodes
@@ -60,16 +60,16 @@ Terraform will be used to provision all of the Equinix Metal resources with base
 Create an inventory directory for your cluster by copying the existing sample and linking the `hosts` script (used to build the inventory based on Terraform state):
 
 ```ShellSession
-cp -LRp contrib/terraform/metal/sample-inventory inventory/$CLUSTER
+cp -LRp contrib/terraform/equinix/sample-inventory inventory/$CLUSTER
 cd inventory/$CLUSTER
-ln -s ../../contrib/terraform/metal/hosts
+ln -s ../../contrib/terraform/equinix/hosts
 ```
 
 This will be the base for subsequent Terraform commands.
 
 #### Equinix Metal API access
 
-Your Equinix Metal API key must be available in the `PACKET_AUTH_TOKEN` environment variable.
+Your Equinix Metal API key must be available in the `METAL_AUTH_TOKEN` environment variable.
 This key is typically stored outside of the code repo since it is considered secret.
 If someone gets this key, they can startup/shutdown hosts in your project!
 
@@ -80,10 +80,12 @@ The Equinix Metal Project ID associated with the key will be set later in `clust
 
 For more information about the API, please see [Equinix Metal API](https://metal.equinix.com/developers/api/).
 
+For more information about terraform provider authentication, please see [the equinix provider documentation](https://registry.terraform.io/providers/equinix/equinix/latest/docs).
+
 Example:
 
 ```ShellSession
-export PACKET_AUTH_TOKEN="Example-API-Token"
+export METAL_AUTH_TOKEN="Example-API-Token"
 ```
 
 Note that to deploy several clusters within the same project you need to use [terraform workspace](https://www.terraform.io/docs/state/workspaces.html#using-workspaces).
@@ -101,7 +103,7 @@ This helps when identifying which hosts are associated with each cluster.
 While the defaults in variables.tf will successfully deploy a cluster, it is recommended to set the following values:
 
 - cluster_name = the name of the inventory directory created above as $CLUSTER
-- metal_project_id = the Equinix Metal Project ID associated with the Equinix Metal API token above
+- equinix_metal_project_id = the Equinix Metal Project ID associated with the Equinix Metal API token above
 
 #### Enable localhost access
 
@@ -119,12 +121,13 @@ Once the Kubespray playbooks are run, a Kubernetes configuration file will be wr
 
 In the cluster's inventory folder, the following files might be created (either by Terraform
 or manually), to prevent you from pushing them accidentally they are in a
-`.gitignore` file in the `terraform/metal` directory :
+`.gitignore` file in the `contrib/terraform/equinix` directory :
 
 - `.terraform`
 - `.tfvars`
 - `.tfstate`
 - `.tfstate.backup`
+- `.lock.hcl`
 
 You can still add them manually if you want to.
 
@@ -135,7 +138,7 @@ plugins. This is accomplished as follows:
 
 ```ShellSession
 cd inventory/$CLUSTER
-terraform init ../../contrib/terraform/metal
+terraform -chdir=../../contrib/terraform/metal init -var-file=cluster.tfvars
 ```
 
 This should finish fairly quickly telling you Terraform has successfully initialized and loaded necessary modules.
@@ -146,7 +149,7 @@ You can apply the Terraform configuration to your cluster with the following com
 issued from your cluster's inventory directory (`inventory/$CLUSTER`):
 
 ```ShellSession
-terraform apply -var-file=cluster.tfvars ../../contrib/terraform/metal
+terraform -chdir=../../contrib/terraform/equinix apply -var-file=cluster.tfvars
 export ANSIBLE_HOST_KEY_CHECKING=False
 ansible-playbook -i hosts ../../cluster.yml
 ```
@@ -156,7 +159,7 @@ ansible-playbook -i hosts ../../cluster.yml
 You can destroy your new cluster with the following command issued from the cluster's inventory directory:
 
 ```ShellSession
-terraform destroy -var-file=cluster.tfvars ../../contrib/terraform/metal
+terraform -chdir=../../contrib/terraform/equinix destroy -var-file=cluster.tfvars
 ```
 
 If you've started the Ansible run, it may also be a good idea to do some manual cleanup:
diff --git a/contrib/terraform/metal/hosts b/contrib/terraform/equinix/hosts
similarity index 100%
rename from contrib/terraform/metal/hosts
rename to contrib/terraform/equinix/hosts
diff --git a/contrib/terraform/metal/kubespray.tf b/contrib/terraform/equinix/kubespray.tf
similarity index 72%
rename from contrib/terraform/metal/kubespray.tf
rename to contrib/terraform/equinix/kubespray.tf
index c8019e5c6..47831a5b7 100644
--- a/contrib/terraform/metal/kubespray.tf
+++ b/contrib/terraform/equinix/kubespray.tf
@@ -1,15 +1,11 @@
-# Configure the Equinix Metal Provider
-provider "metal" {
-}
-
-resource "metal_ssh_key" "k8s" {
+resource "equinix_metal_ssh_key" "k8s" {
   count      = var.public_key_path != "" ? 1 : 0
   name       = "kubernetes-${var.cluster_name}"
   public_key = chomp(file(var.public_key_path))
 }
 
-resource "metal_device" "k8s_master" {
-  depends_on = [metal_ssh_key.k8s]
+resource "equinix_metal_device" "k8s_master" {
+  depends_on = [equinix_metal_ssh_key.k8s]
 
   count            = var.number_of_k8s_masters
   hostname         = "${var.cluster_name}-k8s-master-${count.index + 1}"
@@ -17,12 +13,12 @@ resource "metal_device" "k8s_master" {
   facilities       = [var.facility]
   operating_system = var.operating_system
   billing_cycle    = var.billing_cycle
-  project_id       = var.metal_project_id
+  project_id       = var.equinix_metal_project_id
   tags             = ["cluster-${var.cluster_name}", "k8s_cluster", "kube_control_plane", "etcd", "kube_node"]
 }
 
-resource "metal_device" "k8s_master_no_etcd" {
-  depends_on = [metal_ssh_key.k8s]
+resource "equinix_metal_device" "k8s_master_no_etcd" {
+  depends_on = [equinix_metal_ssh_key.k8s]
 
   count            = var.number_of_k8s_masters_no_etcd
   hostname         = "${var.cluster_name}-k8s-master-${count.index + 1}"
@@ -30,12 +26,12 @@ resource "metal_device" "k8s_master_no_etcd" {
   facilities       = [var.facility]
   operating_system = var.operating_system
   billing_cycle    = var.billing_cycle
-  project_id       = var.metal_project_id
+  project_id       = var.equinix_metal_project_id
   tags             = ["cluster-${var.cluster_name}", "k8s_cluster", "kube_control_plane"]
 }
 
-resource "metal_device" "k8s_etcd" {
-  depends_on = [metal_ssh_key.k8s]
+resource "equinix_metal_device" "k8s_etcd" {
+  depends_on = [equinix_metal_ssh_key.k8s]
 
   count            = var.number_of_etcd
   hostname         = "${var.cluster_name}-etcd-${count.index + 1}"
@@ -43,12 +39,12 @@ resource "metal_device" "k8s_etcd" {
   facilities       = [var.facility]
   operating_system = var.operating_system
   billing_cycle    = var.billing_cycle
-  project_id       = var.metal_project_id
+  project_id       = var.equinix_metal_project_id
   tags             = ["cluster-${var.cluster_name}", "etcd"]
 }
 
-resource "metal_device" "k8s_node" {
-  depends_on = [metal_ssh_key.k8s]
+resource "equinix_metal_device" "k8s_node" {
+  depends_on = [equinix_metal_ssh_key.k8s]
 
   count            = var.number_of_k8s_nodes
   hostname         = "${var.cluster_name}-k8s-node-${count.index + 1}"
@@ -56,7 +52,6 @@ resource "metal_device" "k8s_node" {
   facilities       = [var.facility]
   operating_system = var.operating_system
   billing_cycle    = var.billing_cycle
-  project_id       = var.metal_project_id
+  project_id       = var.equinix_metal_project_id
   tags             = ["cluster-${var.cluster_name}", "k8s_cluster", "kube_node"]
 }
-
diff --git a/contrib/terraform/equinix/output.tf b/contrib/terraform/equinix/output.tf
new file mode 100644
index 000000000..299bced95
--- /dev/null
+++ b/contrib/terraform/equinix/output.tf
@@ -0,0 +1,16 @@
+output "k8s_masters" {
+  value = equinix_metal_device.k8s_master.*.access_public_ipv4
+}
+
+output "k8s_masters_no_etc" {
+  value = equinix_metal_device.k8s_master_no_etcd.*.access_public_ipv4
+}
+
+output "k8s_etcds" {
+  value = equinix_metal_device.k8s_etcd.*.access_public_ipv4
+}
+
+output "k8s_nodes" {
+  value = equinix_metal_device.k8s_node.*.access_public_ipv4
+}
+
diff --git a/contrib/terraform/equinix/provider.tf b/contrib/terraform/equinix/provider.tf
new file mode 100644
index 000000000..74bf0c305
--- /dev/null
+++ b/contrib/terraform/equinix/provider.tf
@@ -0,0 +1,13 @@
+terraform {
+  required_version = ">= 1.0.0"
+  required_providers {
+    equinix = {
+      source  = "equinix/equinix"
+      version = ">=1.11.0"
+    }
+  }
+}
+
+# Configure the Equinix Metal Provider
+provider "equinix" {
+}
diff --git a/contrib/terraform/metal/sample-inventory/cluster.tfvars b/contrib/terraform/equinix/sample-inventory/cluster.tfvars
similarity index 88%
rename from contrib/terraform/metal/sample-inventory/cluster.tfvars
rename to contrib/terraform/equinix/sample-inventory/cluster.tfvars
index f167aeb76..c20aeb231 100644
--- a/contrib/terraform/metal/sample-inventory/cluster.tfvars
+++ b/contrib/terraform/equinix/sample-inventory/cluster.tfvars
@@ -2,7 +2,7 @@
 cluster_name = "mycluster"
 
 # Your Equinix Metal project ID. See hhttps://metal.equinix.com/developers/docs/accounts/
-metal_project_id = "Example-API-Token"
+equinix_metal_project_id = "Example-Project-Id"
 
 # The public SSH key to be uploaded into authorized_keys in bare metal Equinix Metal nodes provisioned
 # leave this value blank if the public key is already setup in the Equinix Metal project
@@ -12,6 +12,9 @@ public_key_path = "~/.ssh/id_rsa.pub"
 # cluster location
 facility = "ewr1"
 
+# operating_system
+operating_system = "ubuntu_22_04"
+
 # standalone etcds
 number_of_etcd = 0
 
diff --git a/contrib/terraform/metal/sample-inventory/group_vars b/contrib/terraform/equinix/sample-inventory/group_vars
similarity index 100%
rename from contrib/terraform/metal/sample-inventory/group_vars
rename to contrib/terraform/equinix/sample-inventory/group_vars
diff --git a/contrib/terraform/metal/variables.tf b/contrib/terraform/equinix/variables.tf
similarity index 92%
rename from contrib/terraform/metal/variables.tf
rename to contrib/terraform/equinix/variables.tf
index f0c9b2889..d8e2d3da1 100644
--- a/contrib/terraform/metal/variables.tf
+++ b/contrib/terraform/equinix/variables.tf
@@ -2,12 +2,12 @@ variable "cluster_name" {
   default = "kubespray"
 }
 
-variable "metal_project_id" {
+variable "equinix_metal_project_id" {
   description = "Your Equinix Metal project ID. See https://metal.equinix.com/developers/docs/accounts/"
 }
 
 variable "operating_system" {
-  default = "ubuntu_20_04"
+  default = "ubuntu_22_04"
 }
 
 variable "public_key_path" {
diff --git a/contrib/terraform/metal/output.tf b/contrib/terraform/metal/output.tf
deleted file mode 100644
index 262d91bb7..000000000
--- a/contrib/terraform/metal/output.tf
+++ /dev/null
@@ -1,16 +0,0 @@
-output "k8s_masters" {
-  value = metal_device.k8s_master.*.access_public_ipv4
-}
-
-output "k8s_masters_no_etc" {
-  value = metal_device.k8s_master_no_etcd.*.access_public_ipv4
-}
-
-output "k8s_etcds" {
-  value = metal_device.k8s_etcd.*.access_public_ipv4
-}
-
-output "k8s_nodes" {
-  value = metal_device.k8s_node.*.access_public_ipv4
-}
-
diff --git a/contrib/terraform/metal/versions.tf b/contrib/terraform/metal/versions.tf
deleted file mode 100644
index 637203f23..000000000
--- a/contrib/terraform/metal/versions.tf
+++ /dev/null
@@ -1,9 +0,0 @@
-
-terraform {
-  required_version = ">= 0.12"
-  required_providers {
-    metal = {
-      source = "equinix/metal"
-    }
-  }
-}
diff --git a/contrib/terraform/terraform.py b/contrib/terraform/terraform.py
index 6c89e1c87..a08df8e6f 100755
--- a/contrib/terraform/terraform.py
+++ b/contrib/terraform/terraform.py
@@ -194,9 +194,19 @@ def parse_bool(string_form):
     else:
         raise ValueError('could not convert %r to a bool' % string_form)
 
-
-@parses('metal_device')
-def metal_device(resource, tfvars=None):
+def sanitize_groups(groups):
+    _groups = []
+    chars_to_replace = ['+', '-', '=', '.', '/', ' ']
+    for i in groups:
+        _i = i
+        for char in chars_to_replace:
+            _i = _i.replace(char, '_')
+        _groups.append(_i)
+    groups.clear()
+    groups.extend(_groups)
+
+@parses('equinix_metal_device')
+def equinix_metal_device(resource, tfvars=None):
     raw_attrs = resource['primary']['attributes']
     name = raw_attrs['hostname']
     groups = []
@@ -220,7 +230,7 @@ def metal_device(resource, tfvars=None):
         'ipv6_address': raw_attrs['network.1.address'],
         'public_ipv6': raw_attrs['network.1.address'],
         'private_ipv4': raw_attrs['network.2.address'],
-        'provider': 'metal',
+        'provider': 'equinix',
     }
 
     if raw_attrs['operating_system'] == 'flatcar_stable':
@@ -228,13 +238,14 @@ def metal_device(resource, tfvars=None):
         attrs.update({'ansible_ssh_user': 'core'})
 
     # add groups based on attrs
-    groups.append('metal_operating_system=' + attrs['operating_system'])
-    groups.append('metal_locked=%s' % attrs['locked'])
-    groups.append('metal_state=' + attrs['state'])
-    groups.append('metal_plan=' + attrs['plan'])
+    groups.append('equinix_metal_operating_system_%s' % attrs['operating_system'])
+    groups.append('equinix_metal_locked_%s' % attrs['locked'])
+    groups.append('equinix_metal_state_%s' % attrs['state'])
+    groups.append('equinix_metal_plan_%s' % attrs['plan'])
 
     # groups specific to kubespray
     groups = groups + attrs['tags']
+    sanitize_groups(groups)
 
     return name, attrs, groups
 
@@ -334,6 +345,8 @@ def openstack_host(resource, module_name):
     for group in attrs['metadata'].get('kubespray_groups', "").split(","):
         groups.append(group)
 
+    sanitize_groups(groups)
+
     return name, attrs, groups
 
 
-- 
GitLab