This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git


The following commit(s) were added to refs/heads/main by this push:
     new 997dbb2  Fix: #117 - Allow Multiple SSH Keypairs (#122)
997dbb2 is described below

commit 997dbb21211c551469b98ca13cbee430c4fcd49e
Author: CodeBleu <[email protected]>
AuthorDate: Tue Jul 30 03:16:06 2024 -0400

    Fix: #117 - Allow Multiple SSH Keypairs (#122)
---
 cloudstack/resource_cloudstack_instance.go      | 53 +++++++++++++--
 cloudstack/resource_cloudstack_instance_test.go | 89 +++++++++++++++++++++++++
 website/docs/r/instance.html.markdown           |  7 +-
 3 files changed, 140 insertions(+), 9 deletions(-)

diff --git a/cloudstack/resource_cloudstack_instance.go 
b/cloudstack/resource_cloudstack_instance.go
index a4e2cae..09e345d 100644
--- a/cloudstack/resource_cloudstack_instance.go
+++ b/cloudstack/resource_cloudstack_instance.go
@@ -139,9 +139,17 @@ func resourceCloudStackInstance() *schema.Resource {
                                ForceNew: true,
                        },
 
-                       "keypair": {
-                               Type:     schema.TypeString,
-                               Optional: true,
+                       "keypair": &schema.Schema{
+                               Type:          schema.TypeString,
+                               Optional:      true,
+                               ConflictsWith: []string{"keypairs"},
+                       },
+
+                       "keypairs": {
+                               Type:          schema.TypeList,
+                               Optional:      true,
+                               Elem:          &schema.Schema{Type: 
schema.TypeString},
+                               ConflictsWith: []string{"keypair"},
                        },
 
                        "host_id": {
@@ -213,6 +221,7 @@ func resourceCloudStackInstance() *schema.Resource {
 }
 
 func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta 
interface{}) error {
+
        cs := meta.(*cloudstack.CloudStackClient)
 
        // Retrieve the service_offering ID
@@ -354,6 +363,14 @@ func resourceCloudStackInstanceCreate(d 
*schema.ResourceData, meta interface{})
                p.SetKeypair(keypair.(string))
        }
 
+       if keypairs, ok := d.GetOk("keypairs"); ok {
+               var keypairStrings []string
+               for _, kp := range keypairs.([]interface{}) {
+                       keypairStrings = append(keypairStrings, 
fmt.Sprintf("%v", kp))
+               }
+               p.SetKeypairs(keypairStrings)
+       }
+
        // If a host_id is supplied, add it to the parameter struct
 
        if hostid, ok := d.GetOk("host_id"); ok {
@@ -493,6 +510,7 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, 
meta interface{}) er
 }
 
 func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta 
interface{}) error {
+
        cs := meta.(*cloudstack.CloudStackClient)
 
        name := d.Get("name").(string)
@@ -537,7 +555,8 @@ func resourceCloudStackInstanceUpdate(d 
*schema.ResourceData, meta interface{})
 
        // Attributes that require reboot to update
        if d.HasChange("name") || d.HasChange("service_offering") || 
d.HasChange("affinity_group_ids") ||
-               d.HasChange("affinity_group_names") || d.HasChange("keypair") 
|| d.HasChange("user_data") {
+               d.HasChange("affinity_group_names") || d.HasChange("keypair") 
|| d.HasChange("keypairs") || d.HasChange("user_data") {
+
                // Before we can actually make these changes, the virtual 
machine must be stopped
                _, err := cs.VirtualMachine.StopVirtualMachine(
                        cs.VirtualMachine.NewStopVirtualMachineParams(d.Id()))
@@ -631,8 +650,8 @@ func resourceCloudStackInstanceUpdate(d 
*schema.ResourceData, meta interface{})
                }
 
                // Check if the keypair has changed and if so, update the 
keypair
-               if d.HasChange("keypair") {
-                       log.Printf("[DEBUG] SSH keypair changed for %s, 
starting update", name)
+               if d.HasChange("keypair") || d.HasChange("keypairs") {
+                       log.Printf("[DEBUG] SSH keypair(s) changed for %s, 
starting update", name)
 
                        p := 
cs.SSH.NewResetSSHKeyForVirtualMachineParams(d.Id())
 
@@ -640,6 +659,26 @@ func resourceCloudStackInstanceUpdate(d 
*schema.ResourceData, meta interface{})
                                p.SetKeypair(keypair.(string))
                        }
 
+                       if keypairs, ok := d.GetOk("keypairs"); ok {
+
+                               // Convert keypairsInterface to []interface{}
+                               keypairsInterfaces := keypairs.([]interface{})
+
+                               // Now, safely convert []interface{} to 
[]string with error handling
+                               strKeyPairs := make([]string, 
len(keypairsInterfaces))
+
+                               for i, v := range keypairsInterfaces {
+                                       switch v := v.(type) {
+                                       case string:
+                                               strKeyPairs[i] = v
+                                       default:
+                                               log.Printf("Value at index %d 
is not a string: %v", i, v)
+                                               continue
+                                       }
+                               }
+                               p.SetKeypairs(strKeyPairs)
+                       }
+
                        // If there is a project supplied, we retrieve and set 
the project id
                        if err := setProjectid(p, cs, d); err != nil {
                                return err
@@ -648,7 +687,7 @@ func resourceCloudStackInstanceUpdate(d 
*schema.ResourceData, meta interface{})
                        _, err = cs.SSH.ResetSSHKeyForVirtualMachine(p)
                        if err != nil {
                                return fmt.Errorf(
-                                       "Error changing the SSH keypair for 
instance %s: %s", name, err)
+                                       "Error changing the SSH keypair(s) for 
instance %s: %s", name, err)
                        }
                }
 
diff --git a/cloudstack/resource_cloudstack_instance_test.go 
b/cloudstack/resource_cloudstack_instance_test.go
index 38307dc..d7eeada 100644
--- a/cloudstack/resource_cloudstack_instance_test.go
+++ b/cloudstack/resource_cloudstack_instance_test.go
@@ -150,6 +150,68 @@ func TestAccCloudStackInstance_keyPair(t *testing.T) {
        })
 }
 
+func TestAccCloudStackInstance_keyPairs(t *testing.T) {
+       var instance cloudstack.VirtualMachine
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackInstanceDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackInstance_keyPairs,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackInstanceExists("cloudstack_instance.foobar", &instance),
+                                       
resource.TestCheckResourceAttr("cloudstack_instance.foobar", "keypairs.#", 
"2"), // Expecting 2 key pairs
+                                       
resource.TestCheckResourceAttr("cloudstack_instance.foobar", "keypairs.0", 
"terraform-test-keypair-foo"),
+                                       
resource.TestCheckResourceAttr("cloudstack_instance.foobar", "keypairs.1", 
"terraform-test-keypair-bar"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackInstance_keyPair_update(t *testing.T) {
+       var instance cloudstack.VirtualMachine
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackInstanceDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackInstance_keyPair,
+                               Check: resource.ComposeTestCheckFunc(
+                                       testAccCheckCloudStackInstanceExists(
+                                               "cloudstack_instance.foobar", 
&instance),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_instance.foobar", 
"keypair", "terraform-test-keypair"),
+                               ),
+                       },
+
+                       {
+                               Config: testAccCloudStackInstance_keyPairs,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackInstanceExists("cloudstack_instance.foobar", &instance),
+                                       
resource.TestCheckResourceAttr("cloudstack_instance.foobar", "keypairs.#", 
"2"), // Expecting 2 key pairs
+                                       
resource.TestCheckResourceAttr("cloudstack_instance.foobar", "keypairs.0", 
"terraform-test-keypair-foo"),
+                                       
resource.TestCheckResourceAttr("cloudstack_instance.foobar", "keypairs.1", 
"terraform-test-keypair-bar"),
+                               ),
+                       },
+
+                       {
+                               Config: testAccCloudStackInstance_keyPair,
+                               Check: resource.ComposeTestCheckFunc(
+                                       testAccCheckCloudStackInstanceExists(
+                                               "cloudstack_instance.foobar", 
&instance),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_instance.foobar", 
"keypair", "terraform-test-keypair"),
+                               ),
+                       },
+               },
+       })
+}
+
 func TestAccCloudStackInstance_project(t *testing.T) {
        var instance cloudstack.VirtualMachine
 
@@ -416,6 +478,33 @@ resource "cloudstack_instance" "foobar" {
   expunge = true
 }`
 
+const testAccCloudStackInstance_keyPairs = `
+resource "cloudstack_network" "foo" {
+  name = "terraform-network"
+  cidr = "10.1.1.0/24"
+  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
+  zone = "Sandbox-simulator"
+}
+
+resource "cloudstack_ssh_keypair" "foo" {
+  name = "terraform-test-keypair-foo"
+}
+
+resource "cloudstack_ssh_keypair" "bar" {
+  name = "terraform-test-keypair-bar"
+}
+
+resource "cloudstack_instance" "foobar" {
+  name = "terraform-test"
+  display_name = "terraform-test"
+  service_offering= "Small Instance"
+  network_id = "${cloudstack_network.foo.id}"
+  template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
+  zone = "Sandbox-simulator"
+  keypairs = ["${cloudstack_ssh_keypair.foo.name}", 
"${cloudstack_ssh_keypair.bar.name}"]
+  expunge = true
+}`
+
 const testAccCloudStackInstance_project = `
 resource "cloudstack_network" "foo" {
   name = "terraform-network"
diff --git a/website/docs/r/instance.html.markdown 
b/website/docs/r/instance.html.markdown
index ee21f69..d4d6187 100644
--- a/website/docs/r/instance.html.markdown
+++ b/website/docs/r/instance.html.markdown
@@ -34,7 +34,7 @@ The following arguments are supported:
 * `service_offering` - (Required) The name or ID of the service offering used
     for this instance.
 
-* `host_id` -  (Optional)  destination Host ID to deploy the VM to - parameter 
available 
+* `host_id` -  (Optional)  destination Host ID to deploy the VM to - parameter 
available
    for root admin only
 
 * `pod_id` -  (Optional) destination Pod ID to deploy the VM to - parameter 
available for root admin only
@@ -82,7 +82,10 @@ The following arguments are supported:
     instance. This can be either plain text or base64 encoded text.
 
 * `keypair` - (Optional) The name of the SSH key pair that will be used to
-    access this instance.
+    access this instance. (Mutual exclusive with keypairs)
+
+* `keypairs` - (Optional) A list of SSH key pair names that will be used to
+    access this instance. (Mutual exclusive with keypair)
 
 * `expunge` - (Optional) This determines if the instance is expunged when it is
     destroyed (defaults false)

Reply via email to