From 987015e9df089877bb22e7394dc60fc9cee1430d Mon Sep 17 00:00:00 2001 From: anacalva <132065767+anacalva@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:51:56 +0100 Subject: [PATCH 1/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d0288bf..91f90d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # stuttgart-things/proxmox-vm -terraform module for creating proxmox vms +Terraform module for creating proxmox vms ## EXAMPLE USAGE TERRAFORM From 11fd6d974a6c41c94a1546e9cd8dac47a8990be2 Mon Sep 17 00:00:00 2001 From: Ana Calva Date: Wed, 20 Mar 2024 14:51:14 +0100 Subject: [PATCH 2/4] Changed runner name --- .github/workflows/release-terraform.yaml | 6 +++--- .github/workflows/validate-terraform.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-terraform.yaml b/.github/workflows/release-terraform.yaml index bfcd80d..5bee56e 100644 --- a/.github/workflows/release-terraform.yaml +++ b/.github/workflows/release-terraform.yaml @@ -17,7 +17,7 @@ jobs: uses: stuttgart-things/stuttgart-things/.github/workflows/validate-terraform.yaml@main with: environment-name: k8s - runs-on: arc-runner-scale-set-proxmox-vm + runs-on: ghr-proxmox-vm-sthings-cicd terraform-version: 1.6 tflint-version: "v0.50.0" continue-error: false @@ -32,5 +32,5 @@ jobs: tag-name: "${{ github.event.inputs.release-tag }}" release-message: "${{ github.event.inputs.release-message }}" environment-name: k8s - runs-on: arc-runner-scale-set-proxmox-vm - continue-error: false \ No newline at end of file + runs-on: ghr-proxmox-vm-sthings-cicd + continue-error: false diff --git a/.github/workflows/validate-terraform.yaml b/.github/workflows/validate-terraform.yaml index bda59cc..8be60bc 100644 --- a/.github/workflows/validate-terraform.yaml +++ b/.github/workflows/validate-terraform.yaml @@ -17,7 +17,7 @@ jobs: uses: stuttgart-things/stuttgart-things/.github/workflows/validate-terraform.yaml@main with: environment-name: k8s - runs-on: arc-runner-scale-set-proxmox-vm + runs-on: ghr-proxmox-vm-sthings-cicd terraform-version: 1.6 tflint-version: "v0.50.0" continue-error: false From 2fe47750492349bd6b5d46db508caa35a77493f8 Mon Sep 17 00:00:00 2001 From: Ana Calva Date: Tue, 19 Mar 2024 15:29:42 +0100 Subject: [PATCH 3/4] added pipeline information --- .github/workflows/vm-creation-terraform.yaml | 111 +++++++++++++++++++ tests/build_tf_file.py | 41 +++++++ tests/templates/module.tpl | 59 ++++++++++ tests/terraform_apply.py | 53 +++++++++ tests/terraform_destroy.py | 23 ++++ tests/test_values.yaml | 14 +++ 6 files changed, 301 insertions(+) create mode 100644 .github/workflows/vm-creation-terraform.yaml create mode 100644 tests/build_tf_file.py create mode 100644 tests/templates/module.tpl create mode 100644 tests/terraform_apply.py create mode 100644 tests/terraform_destroy.py create mode 100644 tests/test_values.yaml diff --git a/.github/workflows/vm-creation-terraform.yaml b/.github/workflows/vm-creation-terraform.yaml new file mode 100644 index 0000000..39c059d --- /dev/null +++ b/.github/workflows/vm-creation-terraform.yaml @@ -0,0 +1,111 @@ +--- +name: Python +on: + push: + branches: + - main + - feature/* + - review/* + - fix/* + +env: + pve_api_url: ${{ secrets.PVE_API_URL }} + pve_api_user: ${{ secrets.PVE_API_USER }} + pve_api_password: ${{ secrets.PVE_API_PASSWORD }} + vm_ssh_user: ${{ secrets.VM_SSH_USER }} + vm_ssh_password: ${{ secrets.VM_SSH_PASSWORD }} + pve_api_tls_verify: ${{ vars.PVE_API_TLS_VERIFY }} + +jobs: + + job_1: + runs-on: self-hosted + + steps: + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install github-action-utils PyYAML Jinja2 + + - name: Run Python to build main.tf from template + run: | + python3 tests/build_tf_file.py + + - name: Upload main.tf file for job 2 + uses: actions/upload-artifact@v4 + with: + name: terraform_main + path: main.tf + + + job_2: + + needs: job_1 + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install github-action-utils python-terraform + + - name: Download main.tf + uses: actions/download-artifact@v4 + with: + name: terraform_main + + - name: Run in Python Terraform Apply + run: | + python3 tests/terraform_apply.py + + - name: Upload tfstate file for cleanup + if: always() + uses: actions/upload-artifact@v4 + with: + name: terraform_state + path: terraform.tfstate + + - name: Run in Python Terraform Destroy + run: | + python3 tests/terraform_destroy.py + + cleanup: + if: ${{ always() }} + needs: job_2 + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install github-action-utils python-terraform + + - name: Download tfstate + uses: actions/download-artifact@v4 + with: + name: terraform_state + + - name: Download main + uses: actions/download-artifact@v4 + with: + name: terraform_main + + - name: Run Python Terraform Destroy + run: | + python3 tests/terraform_destroy.py diff --git a/tests/build_tf_file.py b/tests/build_tf_file.py new file mode 100644 index 0000000..0c6bc8b --- /dev/null +++ b/tests/build_tf_file.py @@ -0,0 +1,41 @@ +import yaml as yaml +import random +import string +from jinja2 import Environment, FileSystemLoader +import github_action_utils as gha_utils + +def random_string_generation(length): + # choose random lowercase letters for unique name + letters = string.ascii_lowercase + result_str = ''.join(random.choice(letters) for i in range(length)) + return result_str + +def write_file(testVars, output_file_name): + environment = Environment(loader=FileSystemLoader("tests/templates/")) + template = environment.get_template("module.tpl") + filename = "main.tf" + content = template.render( + name = output_file_name, + vm_count = random.choice(testVars['vm_count']), + vm_num_cpus = random.choice(testVars['vm_num_cpus']), + pve_datastore = random.choice(testVars['pve_datastore']), + ) + + # Save template + with open(filename, mode="w", encoding="utf-8") as message: + message.write(content) + print(f"... wrote {filename}") + +def main(): + ### Generate Random String for VM name + str_tfvarName = "pipeline-" + random_string_generation(length = 5) + gha_utils.append_job_summary("Unique Name for VM's: " + str_tfvarName) + + ### Import Yaml file with all possible test values + with open('tests/test_values.yaml', 'r') as file: + testVars = yaml.safe_load(file) + print(testVars) + write_file(testVars, str_tfvarName) + +if __name__ == '__main__': + main() diff --git a/tests/templates/module.tpl b/tests/templates/module.tpl new file mode 100644 index 0000000..aeaed30 --- /dev/null +++ b/tests/templates/module.tpl @@ -0,0 +1,59 @@ +module "proxmox-vm" { + source = "git::https://github.com/stuttgart-things/proxmox-vm.git" + #source = "../../repos/proxmox-vm" + pve_api_url = var.pve_api_url + pve_api_user = var.pve_api_user + pve_api_password = var.pve_api_password + pve_api_tls_verify = var.pve_api_tls_verify + + pve_cluster_node = "sthings-pve1" + pve_datastore = "{{ pve_datastore }}" + pve_folder_path = "stuttgart-things" + pve_network = "vmbr103" + vm_count = {{ vm_count }} + vm_name = "{{ name }}" + vm_notes = "vm-info" + vm_template = "ubuntu22" + vm_num_cpus = "{{ vm_num_cpus }}" + vm_memory = "4096" + vm_disk_size = "32G" + vm_ssh_user = var.vm_ssh_user + vm_ssh_password = var.vm_ssh_password +} + +output "ip" { + value = module.proxmox-vm.ip +} + +output "mac" { + value = module.proxmox-vm.mac +} + +output "id" { + value = module.proxmox-vm.id +} + + +variable "pve_api_url" { + description = "url of proxmox api" +} + +variable "pve_api_user" { + description = "username of proxmox api user" +} + +variable "pve_api_password" { + description = "password of proxmox api user" +} + +variable "vm_ssh_user" { + description = "username of proxmox api user" +} + +variable "vm_ssh_password" { + description = "password of proxmox api user" +} + +variable "pve_api_tls_verify" { + description = "proxmox API disable check if cert is valid" +} \ No newline at end of file diff --git a/tests/terraform_apply.py b/tests/terraform_apply.py new file mode 100644 index 0000000..f3133d3 --- /dev/null +++ b/tests/terraform_apply.py @@ -0,0 +1,53 @@ +import python_terraform +import github_action_utils as gha_utils +import os + +def main(): + # Create tfvars variable with secrets + tfvars = {'pve_api_url': os.environ["pve_api_url"], "pve_api_user": os.environ["pve_api_user"], "pve_api_password": os.environ["pve_api_password"], "vm_ssh_user": os.environ["vm_ssh_user"], "vm_ssh_password": os.environ["vm_ssh_password"], "pve_api_tls_verify": os.environ["pve_api_tls_verify"]} + + # Initialize the Terraform working directory + global tf + tf = python_terraform.Terraform(working_dir='.', variables=tfvars) + tf.init() + + # Run terraform Apply and Terraform Destroy + list_ips=run_terraform() + ping_vms(list_ips) + +def run_terraform(): + # Plan the infrastructure changes + tf.plan(capture_output=False) + + # Apply the infrastructure changes + tf.apply(skip_plan=True, capture_output=False) + str_output = tf.output() + gha_utils.append_job_summary("Results after apply:") + list_ips=str_output['ip']['value'] + + # Writes ip's of created vms in output.txt + f = open("output.txt", "a") + gha_utils.append_job_summary("Created VM's:") + for ip in list_ips: + gha_utils.append_job_summary("- " + ip) + f.write(f'{ip}\n') + f.close() + + return list_ips + +def ping_vms(list_ips): + list_responses = [] + for ip in list_ips: + response = os.popen(f"ping -c 4 {ip} ").read() + print(response) + if ("Request timed out." and "unreachable") not in response: + list_responses = list_responses + [ip] + + if not (list_ips == list_responses): + s = set(list_responses) + list_nonActive = [x for x in list_ips if x not in s] + gha_utils.error("The following vm's are non-responsive: " + str(list_nonActive), title="Error Title") + assert False + +if __name__ == '__main__': + main() diff --git a/tests/terraform_destroy.py b/tests/terraform_destroy.py new file mode 100644 index 0000000..bcff91e --- /dev/null +++ b/tests/terraform_destroy.py @@ -0,0 +1,23 @@ +import python_terraform +import github_action_utils as gha_utils +import os + +def main(): + tfvars = {'pve_api_url': os.environ["pve_api_url"], "pve_api_user": os.environ["pve_api_user"], "pve_api_password": os.environ["pve_api_password"], "vm_ssh_user": os.environ["vm_ssh_user"], "vm_ssh_password": os.environ["vm_ssh_password"], "pve_api_tls_verify": os.environ["pve_api_tls_verify"]} + + # Initialize the Terraform working directory + global tf + tf = python_terraform.Terraform(working_dir='.', variables=tfvars) + tf.init() + + run_terraform() + +def run_terraform(): + # Destroys created vms + results = tf.destroy(force=None, auto_approve=True) + print("Destroy Results:") + print(results[1]) + gha_utils.append_job_summary("Results after destroy: " + str(results[1]).splitlines()[-1]) + +if __name__ == '__main__': + main() diff --git a/tests/test_values.yaml b/tests/test_values.yaml new file mode 100644 index 0000000..cd91ad5 --- /dev/null +++ b/tests/test_values.yaml @@ -0,0 +1,14 @@ +--- +# names should be match what the module expects +vm_count: # list or thing of range notation like 1-3; # should pick one of them randomly + - 1 + - 2 + - 3 +vm_num_cpus: # should pick one of them randomly + - 2 + - 4 + - 8 +#vm_name: #E.G. RANDOM NAME W/ LENGHT XY OR PREFIX MAYBE - UP TO YOU BUT THING OF CONSTRAINS LIKE LENGHT OR FORBIDDEN CHARS LIKE _ +pve_datastore: # should pick one of them randomly + - datastore + - v3700 \ No newline at end of file From 04e5e6637577af3ca74791adcdbf834ba355002d Mon Sep 17 00:00:00 2001 From: Ana Calva Date: Thu, 21 Mar 2024 08:35:30 +0100 Subject: [PATCH 4/4] Git runner updated and new pipeline implemented --- .github/workflows/release-terraform.yaml | 11 +++++++++++ .github/workflows/vm-creation-terraform.yaml | 18 +++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release-terraform.yaml b/.github/workflows/release-terraform.yaml index 5bee56e..043a561 100644 --- a/.github/workflows/release-terraform.yaml +++ b/.github/workflows/release-terraform.yaml @@ -34,3 +34,14 @@ jobs: environment-name: k8s runs-on: ghr-proxmox-vm-sthings-cicd continue-error: false + + VM-Creation-Terraform: + if: github.event.ref == 'refs/heads/main' + name: VM-Creation + needs: Release-Terraform + uses: stuttgart-things/stuttgart-things/.github/workflows/vm-creation-terraform.yaml@main + with: + module-name: proxmox-vm + environment-name: k8s + runs-on: ghr-proxmox-vm-sthings-cicd + continue-error: false diff --git a/.github/workflows/vm-creation-terraform.yaml b/.github/workflows/vm-creation-terraform.yaml index 39c059d..0b37832 100644 --- a/.github/workflows/vm-creation-terraform.yaml +++ b/.github/workflows/vm-creation-terraform.yaml @@ -7,6 +7,8 @@ on: - feature/* - review/* - fix/* + pull_request: + types: [opened, reopened] env: pve_api_url: ${{ secrets.PVE_API_URL }} @@ -18,9 +20,8 @@ env: jobs: - job_1: - runs-on: self-hosted - + build-terraform-file: + runs-on: ghr-proxmox-vm-sthings-cicd steps: - uses: actions/checkout@v4 @@ -44,10 +45,9 @@ jobs: path: main.tf - job_2: - - needs: job_1 - runs-on: self-hosted + test-terraform-apply: + needs: build-terraform-file + runs-on: ghr-proxmox-vm-sthings-cicd steps: - uses: actions/checkout@v4 @@ -82,8 +82,8 @@ jobs: cleanup: if: ${{ always() }} - needs: job_2 - runs-on: self-hosted + needs: test-terraform-apply + runs-on: ghr-proxmox-vm-sthings-cicd steps: - uses: actions/checkout@v4