Skip to content

Commit c610f49

Browse files
feat: Add automatic project inheritance for cloudstack_instance from network
Implement automatic project inheritance for cloudstack_instance resource: - Instance inherits project from network_id when no explicit project is set - Only applies to Advanced networking zones - Uses projectid=-1 for universal network lookup across projects - Updates state with inherited project during read operations Changes: - Modified resourceCloudStackInstanceCreate to fetch network and inherit project - Modified resourceCloudStackInstanceRead to set project in state - Added TestAccCloudStackInstance_networkProjectInheritance test case - Updated documentation with inheritance behavior and example All 12 instance acceptance tests passing.
1 parent 3b01c24 commit c610f49

3 files changed

Lines changed: 114 additions & 3 deletions

File tree

cloudstack/resource_cloudstack_instance.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,19 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
364364

365365
if zone.Networktype == "Advanced" {
366366
// Set the default network ID
367-
p.SetNetworkids([]string{d.Get("network_id").(string)})
367+
networkID := d.Get("network_id").(string)
368+
p.SetNetworkids([]string{networkID})
369+
370+
// If no project is explicitly set, try to inherit it from the network
371+
if _, ok := d.GetOk("project"); !ok && networkID != "" {
372+
// Get the network to retrieve its project
373+
// Use projectid=-1 to search across all projects
374+
network, count, err := cs.Network.GetNetworkByID(networkID, cloudstack.WithProject("-1"))
375+
if err == nil && count > 0 && network.Projectid != "" {
376+
log.Printf("[DEBUG] Inheriting project %s from network %s", network.Projectid, networkID)
377+
p.SetProjectid(network.Projectid)
378+
}
379+
}
368380
}
369381

370382
// If there is a ipaddres supplied, add it to the parameter struct
@@ -414,6 +426,7 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
414426
}
415427

416428
// If there is a project supplied, we retrieve and set the project id
429+
// This will override the inherited project from network if explicitly set
417430
if err := setProjectid(p, cs, d); err != nil {
418431
return err
419432
}
@@ -497,10 +510,22 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
497510
cs := meta.(*cloudstack.CloudStackClient)
498511

499512
// Get the virtual machine details
513+
// First try with the project from state (if any)
514+
project := d.Get("project").(string)
500515
vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(
501516
d.Id(),
502-
cloudstack.WithProject(d.Get("project").(string)),
517+
cloudstack.WithProject(project),
503518
)
519+
520+
// If not found and no explicit project was set, try with projectid=-1
521+
// This handles the case where the project was inherited from network
522+
if count == 0 && project == "" {
523+
vm, count, err = cs.VirtualMachine.GetVirtualMachineByID(
524+
d.Id(),
525+
cloudstack.WithProject("-1"),
526+
)
527+
}
528+
504529
if err != nil {
505530
if count == 0 {
506531
log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string))
@@ -516,6 +541,11 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
516541
d.Set("display_name", vm.Displayname)
517542
d.Set("group", vm.Group)
518543

544+
// Set the project if the instance belongs to one
545+
if vm.Project != "" {
546+
d.Set("project", vm.Project)
547+
}
548+
519549
// In some rare cases (when destroying a machine fails) it can happen that
520550
// an instance does not have any attached NIC anymore.
521551
if len(vm.Nic) > 0 {

cloudstack/resource_cloudstack_instance_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,29 @@ func TestAccCloudStackInstance_project(t *testing.T) {
234234
})
235235
}
236236

237+
func TestAccCloudStackInstance_networkProjectInheritance(t *testing.T) {
238+
var instance cloudstack.VirtualMachine
239+
240+
resource.Test(t, resource.TestCase{
241+
PreCheck: func() { testAccPreCheck(t) },
242+
Providers: testAccProviders,
243+
CheckDestroy: testAccCheckCloudStackInstanceDestroy,
244+
Steps: []resource.TestStep{
245+
{
246+
Config: testAccCloudStackInstance_networkProjectInheritance,
247+
Check: resource.ComposeTestCheckFunc(
248+
testAccCheckCloudStackInstanceExists(
249+
"cloudstack_instance.foobar", &instance),
250+
// Verify the project was inherited from the network
251+
resource.TestCheckResourceAttr(
252+
"cloudstack_instance.foobar", "project", "terraform"),
253+
testAccCheckCloudStackInstanceProjectInherited(&instance),
254+
),
255+
},
256+
},
257+
})
258+
}
259+
237260
func TestAccCloudStackInstance_import(t *testing.T) {
238261
resource.Test(t, resource.TestCase{
239262
PreCheck: func() { testAccPreCheck(t) },
@@ -371,6 +394,18 @@ func testAccCheckCloudStackInstanceRenamedAndResized(
371394
}
372395
}
373396

397+
func testAccCheckCloudStackInstanceProjectInherited(
398+
instance *cloudstack.VirtualMachine) resource.TestCheckFunc {
399+
return func(s *terraform.State) error {
400+
401+
if instance.Project != "terraform" {
402+
return fmt.Errorf("Expected project to be 'terraform' (inherited from network), got: %s", instance.Project)
403+
}
404+
405+
return nil
406+
}
407+
}
408+
374409
func testAccCheckCloudStackInstanceDestroy(s *terraform.State) error {
375410
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
376411

@@ -576,3 +611,24 @@ ${random_bytes.string.base64}
576611
EOF
577612
EOFTF
578613
}`
614+
615+
const testAccCloudStackInstance_networkProjectInheritance = `
616+
resource "cloudstack_network" "foo" {
617+
name = "terraform-network"
618+
display_text = "terraform-network"
619+
cidr = "10.1.1.0/24"
620+
network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
621+
project = "terraform"
622+
zone = "Sandbox-simulator"
623+
}
624+
625+
resource "cloudstack_instance" "foobar" {
626+
name = "terraform-test"
627+
display_name = "terraform-test"
628+
service_offering= "Small Instance"
629+
network_id = cloudstack_network.foo.id
630+
template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
631+
zone = cloudstack_network.foo.zone
632+
expunge = true
633+
# Note: project is NOT specified here - it should be inherited from the network
634+
}`

website/docs/r/instance.html.markdown

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,29 @@ resource "cloudstack_instance" "from_template" {
105105
}
106106
```
107107

108+
### Instance with Automatic Project Inheritance
109+
110+
```hcl
111+
# Create a network in a project
112+
resource "cloudstack_network" "project_network" {
113+
name = "project-network"
114+
cidr = "10.1.1.0/24"
115+
network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
116+
project = "my-project"
117+
zone = "zone-1"
118+
}
119+
120+
# Instance automatically inherits project from network
121+
resource "cloudstack_instance" "app" {
122+
name = "app-server"
123+
service_offering = "small"
124+
network_id = cloudstack_network.project_network.id
125+
template = "CentOS 7"
126+
zone = cloudstack_network.project_network.zone
127+
# project is automatically inherited from the network
128+
}
129+
```
130+
108131
## Argument Reference
109132

110133
The following arguments are supported:
@@ -164,7 +187,9 @@ The following arguments are supported:
164187
this instance. Changing this forces a new resource to be created.
165188

166189
* `project` - (Optional) The name or ID of the project to deploy this
167-
instance to. Changing this forces a new resource to be created.
190+
instance to. Changing this forces a new resource to be created. If not
191+
specified and `network_id` is provided, the project will be automatically
192+
inherited from the network.
168193

169194
* `zone` - (Required) The name or ID of the zone where this instance will be
170195
created. Changing this forces a new resource to be created.

0 commit comments

Comments
 (0)