Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 60 additions & 19 deletions opentofu/envs/dev/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion opentofu/envs/dev/main.tofu
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ terraform {
required_providers {
vultr = {
source = "vultr/vultr"
version = "= 2.22.1"
version = "= 2.28.1"
}
cloudflare = {
source = "cloudflare/cloudflare"
Expand Down
81 changes: 81 additions & 0 deletions opentofu/envs/dev/tests/vultr-block-storage.tofutest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
mock_provider "vultr" {
mock_resource "vultr_block_storage" {
defaults = {
id = "test-block-storage-id"
mount_id = "test-instance-id"
}
}
}

run "block_storage_defaults" {
command = plan

module {
source = "../../modules/vultr/block_storage"
}

variables {
region = "ewr"
mount_instance_id = "test-instance-id"
}

assert {
condition = vultr_block_storage.this.region == "ewr"
error_message = "Block storage region should match provided value"
}

assert {
condition = vultr_block_storage.this.size_gb == 25
error_message = "Block storage size should default to 25 GB"
}

assert {
condition = vultr_block_storage.this.label == "ghost-block"
error_message = "Block storage label should default to ghost-block"
}

assert {
condition = vultr_block_storage.this.live == true
error_message = "Block storage live should be true"
}

assert {
condition = vultr_block_storage.this.attached_to_instance == "test-instance-id"
error_message = "Block storage should be attached to the provided instance ID"
}
}

run "block_storage_custom_values" {
command = plan

module {
source = "../../modules/vultr/block_storage"
}

variables {
region = "lax"
size_gb = 50
label = "custom-label"
mount_instance_id = "custom-instance-id"
}

assert {
condition = vultr_block_storage.this.region == "lax"
error_message = "Block storage region should match provided value"
}

assert {
condition = vultr_block_storage.this.size_gb == 50
error_message = "Block storage size should match provided value"
}

assert {
condition = vultr_block_storage.this.label == "custom-label"
error_message = "Block storage label should match provided value"
}

assert {
condition = vultr_block_storage.this.attached_to_instance == "custom-instance-id"
error_message = "Block storage should be attached to the provided instance ID"
}
}
91 changes: 6 additions & 85 deletions opentofu/modules/vultr/block_storage/main.tofu
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,15 @@ terraform {
required_providers {
vultr = {
source = "vultr/vultr"
version = "= 2.22.1"
}
null = {
source = "hashicorp/null"
version = "~> 3.0"
version = "= 2.28.1"
}
}
}

# Block storage resource - attachment is handled separately via null_resource
# due to Vultr provider bug: https://github.com/vultr/terraform-provider-vultr/issues/660
resource "vultr_block_storage" "this" {
region = var.region
size_gb = var.size_gb
label = var.label
live = true

# Ignore attachment changes - managed by null_resource below to work around
# provider bug. This prevents Terraform from trying to detach/reattach.
lifecycle {
ignore_changes = [attached_to_instance]
}
}

# Workaround for Vultr provider bug #660
# The provider fails to attach block storage to recreated instances with:
# Error: error getting block storage: {"error":"Nothing to change","status":400}
#
# This null_resource uses the Vultr CLI to handle attachment.
# It inherits VULTR_API_KEY from the environment (set via TF_VAR_vultr_api_key in CI).
resource "null_resource" "attach_block_storage" {
# Note: count removed because it cannot depend on values unknown at plan time
# (instance_id is unknown when instance is being recreated)

triggers = {
instance_id = var.mount_instance_id
block_storage_id = vultr_block_storage.this.id
}

provisioner "local-exec" {
command = <<-EOT
set -e

# API key is inherited from environment (VULTR_API_KEY or TF_VAR_vultr_api_key)
export VULTR_API_KEY="$${VULTR_API_KEY:-$TF_VAR_vultr_api_key}"
if [ -z "$VULTR_API_KEY" ]; then
echo "ERROR: VULTR_API_KEY or TF_VAR_vultr_api_key must be set"
exit 1
fi

BLOCK_STORAGE_ID="${vultr_block_storage.this.id}"
INSTANCE_ID="${var.mount_instance_id}"

echo "Waiting for instance to be fully ready..."
sleep 30

echo "Checking current block storage state..."
CURRENT_ATTACHED=$(vultr-cli block-storage get "$BLOCK_STORAGE_ID" | tail -n1 | awk '{print $3}')
echo "Currently attached to: $${CURRENT_ATTACHED:-none}"

# If attached to something (possibly stale/deleted instance), detach first
if [ -n "$CURRENT_ATTACHED" ] && [ "$CURRENT_ATTACHED" != "$INSTANCE_ID" ]; then
echo "Detaching from previous instance..."
vultr-cli block-storage detach "$BLOCK_STORAGE_ID" --live || true

echo "Waiting for detach to complete..."
sleep 15
fi

# Attach to the new instance
echo "Attaching block storage to instance $INSTANCE_ID..."
vultr-cli block-storage attach "$BLOCK_STORAGE_ID" --instance="$INSTANCE_ID" --live || true

# Verify attachment
echo "Verifying block storage attachment..."
sleep 5
VERIFIED_ATTACHED=$(vultr-cli block-storage get "$BLOCK_STORAGE_ID" | tail -n1 | awk '{print $3}')

if [ "$VERIFIED_ATTACHED" = "$INSTANCE_ID" ]; then
echo "Verification successful: block storage is attached to $INSTANCE_ID"
else
echo "WARNING: Block storage may not be properly attached"
echo "Expected: $INSTANCE_ID"
echo "Actual: $${VERIFIED_ATTACHED:-none}"
exit 1
fi
EOT
}

depends_on = [vultr_block_storage.this]
region = var.region
size_gb = var.size_gb
label = var.label
live = true
attached_to_instance = var.mount_instance_id
}
2 changes: 1 addition & 1 deletion opentofu/modules/vultr/firewall/main.tofu
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
vultr = {
source = "vultr/vultr"
version = "= 2.22.1"
version = "= 2.28.1"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion opentofu/modules/vultr/instance/main.tofu
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
terraform {
required_providers {
vultr = { source = "vultr/vultr", version = "= 2.22.1" }
vultr = { source = "vultr/vultr", version = "= 2.28.1" }
ct = {
source = "poseidon/ct"
version = "0.14.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# GHO-109: trigger rebuild to verify block storage attachment with native provider support
[Unit]
Description=Docker-Compose Ghost CMS Container
After=docker.service var-mnt-storage.mount tinybird-provision.service infisical-secrets.service
Expand Down
Loading