Skip to content

Commit 2707792

Browse files
HiranAdikariclaude
andcommitted
Address CodeRabbit review comments on PR #25
bootstrap/main.tf: - Add check block: create_ssh_key must be true when create_cloudinit_secret is true (cloud-init template embeds the generated SSH public key) - Add check block: existing_cloudinit_secret_name must be non-empty when create_cloudinit_secret = false bootstrap/examples/basic/main.tf: - Replace removed rancher_admin_password with bootstrap_password management/cluster-roles/main.tf: - Split harvesterhci.io rule: virtualmachineimages stays read-only; keypairs gets create+delete so vm-manager can inject/remove SSH keys via workloads/vm without RBAC denials management/cluster-roles/README.md: - Add network-manager to roles table and network_manager_role_id to outputs table to document the cluster-scoped role management/harvester-integration/examples/basic/main.tf: - Remove kubernetes provider (no longer required by module) - Replace removed rancher_hostname/rancher_lb_ip with cloud_credential_name and cluster_labels; update module source to wso2 org management/networking/examples/basic/main.tf: management/storage/examples/basic/main.tf: - Bump harvester provider from ~> 0.6.0 to ~> 1.7 to match all other modules and prevent init failures from conflicting constraints workloads/vm/main.tf: - Replace sensitive-derived count with non-sensitive create_ssh_key bool - Make wait_for_lease configurable via variable (default true) workloads/vm/variables.tf: - Add create_ssh_key and wait_for_lease variables workloads/vm/README.md: - Fix cloud-init resource description (no standalone secret is created) - Use file(pathexpand("~/.ssh/...")) in examples (file() does not expand ~) - Add create_ssh_key and wait_for_lease to inputs table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c8ff835 commit 2707792

File tree

10 files changed

+66
-31
lines changed

10 files changed

+66
-31
lines changed

modules/bootstrap/examples/basic/main.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ module "bootstrap" {
4747

4848
# Credentials – supply via tfvars or environment variables; never hardcode
4949
vm_password = var.vm_password
50-
rancher_admin_password = var.rancher_admin_password
50+
bootstrap_password = var.bootstrap_password
5151

5252
rancher_hostname = "rancher.example.internal"
5353

@@ -63,7 +63,7 @@ variable "vm_password" {
6363
sensitive = true
6464
}
6565

66-
variable "rancher_admin_password" {
66+
variable "bootstrap_password" {
6767
type = string
6868
sensitive = true
6969
}

modules/bootstrap/main.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ locals {
5353
cloudinit_secret_name = var.create_cloudinit_secret ? harvester_cloudinit_secret.cloudinit[0].name : var.existing_cloudinit_secret_name
5454
}
5555

56+
# ── Input validation ──────────────────────────────────────────────────────────
57+
58+
# The cloud-init template embeds the generated SSH public key. If create_ssh_key
59+
# is false the tls_private_key resource is empty, causing an invalid-index error.
60+
check "ssh_key_required_for_cloudinit" {
61+
assert {
62+
condition = !var.create_cloudinit_secret || var.create_ssh_key
63+
error_message = "create_ssh_key must be true when create_cloudinit_secret is true (the cloud-init template embeds the generated SSH public key)."
64+
}
65+
}
66+
67+
# When reusing an existing cloud-init secret, the name must be provided.
68+
check "existing_cloudinit_secret_name_required" {
69+
assert {
70+
condition = var.create_cloudinit_secret || var.existing_cloudinit_secret_name != ""
71+
error_message = "existing_cloudinit_secret_name is required when create_cloudinit_secret = false."
72+
}
73+
}
74+
5675
# ── Rancher server VM ─────────────────────────────────────────────────────────
5776
resource "harvester_virtualmachine" "rancher_server" {
5877
count = var.node_count

modules/management/cluster-roles/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module is not required.
1717
| Role Name | Context | Purpose |
1818
|-----------|---------|---------|
1919
| `vm-manager` | project | Full lifecycle management of VMs: create, configure, start/stop/restart, console, and delete. |
20+
| `network-manager` | cluster | Manage Harvester VLAN infrastructure and NetworkAttachmentDefinitions. Bind only via `rancher2_cluster_role_template_binding`. |
2021
| `vm-metrics-observer` | project | Read-only access to VM status and metrics. No mutating verbs. |
2122

2223
### `vm-manager` Permissions
@@ -27,7 +28,8 @@ module is not required.
2728
| `subresources.kubevirt.io` | `virtualmachines/start`, `virtualmachines/stop`, `virtualmachines/restart`, `virtualmachines/migrate`, `virtualmachineinstances/vnc`, `virtualmachineinstances/console`, `virtualmachineinstances/portforward`, `virtualmachineinstances/pause`, `virtualmachineinstances/unpause` | `get`, `update` |
2829
| `subresources.kubevirt.io` | `virtualmachineinstances/metrics` | `get` |
2930
| `cdi.kubevirt.io` | `datavolumes` | `get`, `list`, `watch`, `create`, `update`, `patch`, `delete` |
30-
| `harvesterhci.io` | `virtualmachineimages`, `keypairs` | `get`, `list`, `watch` |
31+
| `harvesterhci.io` | `virtualmachineimages` | `get`, `list`, `watch` |
32+
| `harvesterhci.io` | `keypairs` | `get`, `list`, `watch`, `create`, `delete` |
3133
| `""` (core) | `secrets`, `configmaps` | `get`, `list`, `watch`, `create`, `update`, `patch`, `delete` |
3234
| `""` (core) | `services/proxy` | `get` |
3335

@@ -94,6 +96,7 @@ module "tenant_space_iam_observer" {
9496
| Name | Description |
9597
|------|-------------|
9698
| `vm_manager_role_id` | Role template ID for `vm-manager`. Pass to `tenant-space` `group_role_bindings`. |
99+
| `network_manager_role_id` | Role template ID for `network-manager` (cluster-scoped). Pass to `rancher2_cluster_role_template_binding`. |
97100
| `vm_metrics_observer_role_id` | Role template ID for `vm-metrics-observer`. Pass to `tenant-space` `group_role_bindings`. |
98101

99102
## Notes

modules/management/cluster-roles/main.tf

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,20 @@ resource "rancher2_role_template" "vm_manager" {
3838
verbs = ["get", "list", "watch", "create", "update", "patch", "delete"]
3939
}
4040

41-
# Read access to VM images and SSH keypairs available in the namespace
41+
# Read access to VM images available in the namespace
4242
rules {
4343
api_groups = ["harvesterhci.io"]
44-
resources = ["virtualmachineimages", "keypairs"]
44+
resources = ["virtualmachineimages"]
4545
verbs = ["get", "list", "watch"]
4646
}
4747

48+
# SSH keypairs — full CRUD so tenants can inject and remove keys via workloads/vm
49+
rules {
50+
api_groups = ["harvesterhci.io"]
51+
resources = ["keypairs"]
52+
verbs = ["get", "list", "watch", "create", "delete"]
53+
}
54+
4855
# Cloud-init secrets and SSH key secrets
4956
rules {
5057
api_groups = [""]

modules/management/harvester-integration/examples/basic/main.tf

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22
#
33
# This example integrates a Harvester cluster into an existing Rancher server
44
# by enabling the Harvester feature flag, installing the UI extension, creating
5-
# a cloud credential, patching CoreDNS so Harvester can resolve the internal
6-
# Rancher FQDN, and applying the registration manifest.
5+
# a cloud credential, and applying the registration manifest.
76
#
87
# Prerequisites:
98
# - Rancher deployed and accessible (e.g. via the bootstrap module)
109
# - The Harvester kubeconfig available locally
1110
# - kubectl available in PATH (used by local-exec provisioners)
1211
# - The rancher2 provider configured with your Rancher URL and access key
1312
# - The harvester provider configured with your Harvester kubeconfig
14-
# - The kubernetes provider configured against the Harvester cluster
1513

1614
terraform {
1715
required_version = ">= 1.3"
@@ -25,10 +23,6 @@ terraform {
2523
source = "harvester/harvester"
2624
version = "~> 1.7"
2725
}
28-
kubernetes = {
29-
source = "hashicorp/kubernetes"
30-
version = "~> 3.0"
31-
}
3226
}
3327
}
3428

@@ -39,18 +33,14 @@ provider "rancher2" {
3933
}
4034

4135
provider "harvester" {
42-
kubeconfig = file("~/.kube/harvester-config")
43-
}
44-
45-
provider "kubernetes" {
46-
config_path = "~/.kube/harvester-config"
36+
kubeconfig = file(pathexpand("~/.kube/harvester-config"))
4737
}
4838

4939
module "harvester_integration" {
50-
source = "github.com/wso2-enterprise/open-cloud-datacenter//modules/management/harvester-integration?ref=v0.1.0"
40+
source = "github.com/wso2/open-cloud-datacenter//modules/management/harvester-integration?ref=v0.2.0"
5141

52-
harvester_kubeconfig = file("~/.kube/harvester-config")
42+
harvester_kubeconfig = file(pathexpand("~/.kube/harvester-config"))
5343
harvester_cluster_name = "harvester-hci"
54-
rancher_hostname = "rancher.example.internal"
55-
rancher_lb_ip = "192.168.10.10"
44+
cloud_credential_name = "harvester-local-creds"
45+
cluster_labels = {}
5646
}

modules/management/networking/examples/basic/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ terraform {
1313
required_providers {
1414
harvester = {
1515
source = "harvester/harvester"
16-
version = "~> 0.6.0"
16+
version = "~> 1.7"
1717
}
1818
}
1919
}

modules/management/storage/examples/basic/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ terraform {
1515
required_providers {
1616
harvester = {
1717
source = "harvester/harvester"
18-
version = "~> 0.6.0"
18+
version = "~> 1.7"
1919
}
2020
}
2121
}

modules/workloads/vm/README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ by product teams who have been granted a `tenant-space` with the `vm-manager` ro
66
This module creates:
77
- A `harvester_virtualmachine` with configurable CPU, memory, disk, and networking
88
- Optionally, a `harvester_ssh_key` (when `ssh_public_key` is set)
9-
- Optionally, a `harvester_cloudinit_secret` (when `user_data` is set)
9+
- Optionally, cloud-init user-data/network-data attached through the VM resource (when `user_data` is set)
1010

1111
## When to Use
1212

@@ -57,7 +57,7 @@ module "app_vm" {
5757
disk_size = "80Gi"
5858
image_name = data.terraform_remote_state.management.outputs.image_ids["ubuntu-22-04"]
5959
network_name = "iam-team-vlan"
60-
ssh_public_key = file("~/.ssh/id_rsa.pub")
60+
ssh_public_key = file(pathexpand("~/.ssh/id_rsa.pub"))
6161
}
6262
```
6363

@@ -71,7 +71,7 @@ module "app_vm" {
7171
namespace = "iam-team-ns"
7272
image_name = data.terraform_remote_state.management.outputs.image_ids["ubuntu-22-04"]
7373
network_name = "iam-team-vlan"
74-
ssh_public_key = file("~/.ssh/id_rsa.pub")
74+
ssh_public_key = file(pathexpand("~/.ssh/id_rsa.pub"))
7575
7676
user_data = <<-EOT
7777
#cloud-config
@@ -112,7 +112,9 @@ locals {
112112
| `image_name` | Harvester image in `namespace/name` format | `string` || yes |
113113
| `network_name` | Harvester network attachment name | `string` || yes |
114114
| `run_strategy` | `RerunOnFailure`, `Always`, `Halted`, or `Manual` | `string` | `"RerunOnFailure"` | no |
115-
| `ssh_public_key` | SSH public key content. Creates a `harvester_ssh_key` when set. | `string` | `null` | no |
115+
| `ssh_public_key` | SSH public key content. Used when `create_ssh_key = true`. | `string` | `null` | no |
116+
| `create_ssh_key` | When true, create a `harvester_ssh_key` from `ssh_public_key`. | `bool` | `false` | no |
117+
| `wait_for_lease` | Wait for IP lease on primary NIC. Set false for static cloud-init IPs. | `bool` | `true` | no |
116118
| `user_data` | Cloud-init user-data YAML. Creates a cloud-init secret when set. | `string` | `null` | no |
117119
| `network_data` | Cloud-init network-data config (requires `user_data` to be set). | `string` | `""` | no |
118120

@@ -132,7 +134,9 @@ locals {
132134
- `network_name` must match a network attachment definition in the same Harvester cluster
133135
(created by `management/networking`).
134136
- The VM's IP address is available in `network_interfaces[0].ip_address` after the
135-
lease is obtained (Terraform will wait due to `wait_for_lease = true`).
137+
lease is obtained (requires `wait_for_lease = true`, which is the default).
138+
Set `wait_for_lease = false` when using static IPs via cloud-init `network_data`
139+
without qemu-guest-agent.
136140
- The `vm-manager` custom role from `management/cluster-roles` must be bound to the
137141
team's group in their `tenant-space` before they can create VMs in the namespace.
138142
- Removing this module or running `terraform destroy` **deletes the VM and its disk**.

modules/workloads/vm/main.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Optional SSH key — created only when ssh_public_key is provided.
22
resource "harvester_ssh_key" "this" {
3-
count = var.ssh_public_key != null ? 1 : 0
3+
count = var.create_ssh_key ? 1 : 0
44

55
name = "${var.name}-key"
66
namespace = var.namespace
@@ -18,11 +18,11 @@ resource "harvester_virtualmachine" "this" {
1818
run_strategy = var.run_strategy
1919
machine_type = "q35"
2020

21-
ssh_keys = var.ssh_public_key != null ? [harvester_ssh_key.this[0].id] : []
21+
ssh_keys = var.create_ssh_key ? [harvester_ssh_key.this[0].id] : []
2222

2323
network_interface {
2424
name = "nic-1"
25-
wait_for_lease = true
25+
wait_for_lease = var.wait_for_lease
2626
network_name = var.network_name
2727
}
2828

modules/workloads/vm/variables.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,15 @@ variable "network_data" {
6464
description = "Cloud-init network-data config. Ignored if user_data is null."
6565
default = ""
6666
}
67+
68+
variable "create_ssh_key" {
69+
type = bool
70+
description = "When true, create a harvester_ssh_key from ssh_public_key and attach it to the VM. Must be true for ssh_public_key to have any effect."
71+
default = false
72+
}
73+
74+
variable "wait_for_lease" {
75+
type = bool
76+
description = "Whether Terraform should wait for an IP lease on the primary NIC. Set to false when using static IPs via cloud-init network_data without qemu-guest-agent."
77+
default = true
78+
}

0 commit comments

Comments
 (0)