Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
71 changes: 69 additions & 2 deletions cloudstack/resource_cloudstack_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,32 @@ func resourceCloudStackAccount() *schema.Resource {
"email": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Comment thread
kristofer-atlas marked this conversation as resolved.
Outdated
},
"first_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"last_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"password": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
Comment thread
kristofer-atlas marked this conversation as resolved.
"username": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"account_type": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"role_id": {
Type: schema.TypeString,
Expand Down Expand Up @@ -110,9 +116,70 @@ func resourceCloudStackAccountCreate(d *schema.ResourceData, meta interface{}) e
return resourceCloudStackAccountRead(d, meta)
}

func resourceCloudStackAccountRead(d *schema.ResourceData, meta interface{}) error { return nil }
func resourceCloudStackAccountRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

log.Printf("[DEBUG] Reading Account %s", d.Id())

p := cs.Account.NewListAccountsParams()
p.SetId(d.Id())

accounts, err := cs.Account.ListAccounts(p)
if err != nil {
return fmt.Errorf("Error retrieving Account %s: %s", d.Id(), err)
}

if accounts.Count == 0 {
log.Printf("[DEBUG] Account %s does no longer exist", d.Id())
d.SetId("")
return nil
}

account := accounts.Accounts[0]

d.Set("account_type", account.Accounttype)
d.Set("role_id", account.Roleid)
d.Set("account", account.Name)
d.Set("domain_id", account.Domainid)

if len(account.User) > 0 {
user := account.User[0]
d.Set("email", user.Email)
d.Set("first_name", user.Firstname)
d.Set("last_name", user.Lastname)
d.Set("username", user.Username)
}

log.Printf("[DEBUG] Account %s successfully read", d.Id())
return nil
}

func resourceCloudStackAccountUpdate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

log.Printf("[DEBUG] Updating Account %s", d.Id())

p := cs.Account.NewUpdateAccountParams()
p.SetId(d.Id())

if d.HasChange("role_id") {
p.SetRoleid(d.Get("role_id").(string))
}
if d.HasChange("account") {
p.SetNewname(d.Get("account").(string))
}
if d.HasChange("domain_id") {
p.SetDomainid(d.Get("domain_id").(string))
}

func resourceCloudStackAccountUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
_, err := cs.Account.UpdateAccount(p)
if err != nil {
return fmt.Errorf("Error updating Account %s: %s", d.Id(), err)
}

log.Printf("[DEBUG] Account %s successfully updated", d.Id())
return resourceCloudStackAccountRead(d, meta)
}

func resourceCloudStackAccountDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
Expand Down
58 changes: 55 additions & 3 deletions cloudstack/resource_cloudstack_disk_offering.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package cloudstack

import (
"fmt"
"log"

"github.com/apache/cloudstack-go/v2/cloudstack"
Expand Down Expand Up @@ -72,8 +73,59 @@ func resourceCloudStackDiskOfferingCreate(d *schema.ResourceData, meta interface
return resourceCloudStackDiskOfferingRead(d, meta)
}

func resourceCloudStackDiskOfferingRead(d *schema.ResourceData, meta interface{}) error { return nil }
func resourceCloudStackDiskOfferingRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

log.Printf("[DEBUG] Retrieving disk offering %s", d.Get("name").(string))

offering, count, err := cs.DiskOffering.GetDiskOfferingByID(d.Id())
if err != nil {
if count == 0 {
log.Printf("[DEBUG] Disk offering %s does no longer exist", d.Get("name").(string))
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving disk offering %s: %s", d.Id(), err)
}

d.Set("name", offering.Name)
d.Set("display_text", offering.Displaytext)
d.Set("disk_size", offering.Disksize)

return nil
}

func resourceCloudStackDiskOfferingUpdate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

p := cs.DiskOffering.NewUpdateDiskOfferingParams(d.Id())

if d.HasChange("name") {
p.SetName(d.Get("name").(string))
}
if d.HasChange("display_text") {
p.SetDisplaytext(d.Get("display_text").(string))
}
Comment thread
kristofer-atlas marked this conversation as resolved.

func resourceCloudStackDiskOfferingUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
log.Printf("[DEBUG] Updating disk offering %s", d.Get("name").(string))
_, err := cs.DiskOffering.UpdateDiskOffering(p)
if err != nil {
return fmt.Errorf("Error updating disk offering: %s", err)
}

func resourceCloudStackDiskOfferingDelete(d *schema.ResourceData, meta interface{}) error { return nil }
return resourceCloudStackDiskOfferingRead(d, meta)
}

func resourceCloudStackDiskOfferingDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

p := cs.DiskOffering.NewDeleteDiskOfferingParams(d.Id())

log.Printf("[DEBUG] Deleting disk offering %s", d.Get("name").(string))
_, err := cs.DiskOffering.DeleteDiskOffering(p)
if err != nil {
return fmt.Errorf("Error deleting disk offering: %s", err)
}

return nil
}
57 changes: 55 additions & 2 deletions cloudstack/resource_cloudstack_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,62 @@ func resourceCloudStackDomainCreate(d *schema.ResourceData, meta interface{}) er
return resourceCloudStackDomainRead(d, meta)
}

func resourceCloudStackDomainRead(d *schema.ResourceData, meta interface{}) error { return nil }
func resourceCloudStackDomainRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

log.Printf("[DEBUG] Reading Domain %s", d.Id())

p := cs.Domain.NewListDomainsParams()
p.SetId(d.Id())

domains, err := cs.Domain.ListDomains(p)
if err != nil {
return fmt.Errorf("Error reading Domain %s: %s", d.Id(), err)
}

if domains.Count == 0 {
log.Printf("[DEBUG] Domain %s does no longer exist", d.Id())
d.SetId("")
return nil
}

domain := domains.Domains[0]
log.Printf("[DEBUG] Domain %s found: %s", d.Id(), domain.Name)

d.Set("name", domain.Name)
d.Set("domain_id", domain.Id)
d.Set("network_domain", domain.Networkdomain)
Comment thread
kristofer-atlas marked this conversation as resolved.
d.Set("parent_domain_id", domain.Parentdomainid)

return nil
}

func resourceCloudStackDomainUpdate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

name := d.Get("name").(string)

if d.HasChange("name") || d.HasChange("network_domain") {
p := cs.Domain.NewUpdateDomainParams(d.Id())

func resourceCloudStackDomainUpdate(d *schema.ResourceData, meta interface{}) error { return nil }
if d.HasChange("name") {
p.SetName(name)
}

if d.HasChange("network_domain") {
p.SetNetworkdomain(d.Get("network_domain").(string))
}

log.Printf("[DEBUG] Updating Domain %s", name)
_, err := cs.Domain.UpdateDomain(p)
if err != nil {
return fmt.Errorf("Error updating Domain %s: %s", name, err)
}
log.Printf("[DEBUG] Domain %s successfully updated", name)
}

return resourceCloudStackDomainRead(d, meta)
}

func resourceCloudStackDomainDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
Expand Down
39 changes: 39 additions & 0 deletions cloudstack/resource_cloudstack_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,28 @@ func resourceCloudStackUserCreate(d *schema.ResourceData, meta interface{}) erro
}

func resourceCloudStackUserUpdate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

p := cs.User.NewUpdateUserParams(d.Id())

if d.HasChange("email") {
p.SetEmail(d.Get("email").(string))
}
if d.HasChange("first_name") {
p.SetFirstname(d.Get("first_name").(string))
}
if d.HasChange("last_name") {
p.SetLastname(d.Get("last_name").(string))
}
if d.HasChange("password") {
p.SetPassword(d.Get("password").(string))
}
Comment thread
kristofer-atlas marked this conversation as resolved.

_, err := cs.User.UpdateUser(p)
if err != nil {
return err
}
Comment thread
kristofer-atlas marked this conversation as resolved.

return resourceCloudStackUserRead(d, meta)
}

Expand All @@ -106,5 +128,22 @@ func resourceCloudStackUserDelete(d *schema.ResourceData, meta interface{}) erro
}

func resourceCloudStackUserRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

user, count, err := cs.User.GetUserByID(d.Id())
if err != nil {
if count == 0 {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading User %s: %s", d.Id(), err)
}

d.Set("account", user.Account)
d.Set("email", user.Email)
d.Set("first_name", user.Firstname)
d.Set("last_name", user.Lastname)
d.Set("username", user.Username)

return nil
}
42 changes: 42 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# CloudStack Terraform Provider Test Suites

This directory contains test configurations for validating the CloudStack Terraform Provider.

## Test Suites

### comprehensive/
Full provider feature test covering VMs, networks, volumes, firewall rules, load balancers, and more.

### networking/
Focused test for networking configuration with egress rules.

## Prerequisites

1. CloudStack API credentials:
```bash
export CLOUDSTACK_API_URL="your-api-url"
export CLOUDSTACK_API_KEY="your-api-key"
export CLOUDSTACK_SECRET_KEY="your-secret-key"
```

2. Terraform 1.0+

3. Built provider (for local testing):
```bash
make install
```

## Running Tests

```bash
cd tests/comprehensive # or tests/networking
terraform init
terraform plan
terraform apply
```

## Cleanup

```bash
terraform destroy
```
45 changes: 45 additions & 0 deletions tests/comprehensive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Comprehensive Provider Test Suite

Tests the CloudStack Terraform Provider capabilities and identifies limitations.

## Test Coverage

1. SSH Keypair
2. VPC and Networking
3. Isolated Network with Source NAT
4. IP Address allocation
5. Security Groups and Firewall rules
6. Network ACL
7. VM Deployment (2 VMs with cloud-init, SSH keys, affinity groups, tags)
8. Affinity Groups
9. Volumes
10. Volume Attachments
11. Port Forwarding
12. Load Balancer
13. Static NAT
14. Secondary IP

## Usage

```bash
terraform init
terraform plan
terraform apply
terraform destroy
```

## Configuration

Copy terraform.tfvars.example to terraform.tfvars and configure:

- api_url - CloudStack API endpoint
- api_key - API key
- secret_key - API secret
- zone - Zone name
- network_offering - Network offering name
- service_offering - VM service offering
- template - Template name

## Known Limitations

Some resources have stub implementations or missing update functions. See provider documentation for details.
Loading
Loading