-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #74 from cloud-ca/vpn
Added Remote Access VPN functionality
- Loading branch information
Showing
19 changed files
with
841 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package cloudca | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/cloud-ca/go-cloudca" | ||
"github.com/cloud-ca/go-cloudca/api" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceCloudcaVpn() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceCloudcaVpnCreate, | ||
Read: resourceCloudcaVpnRead, | ||
Delete: resourceCloudcaVpnDelete, | ||
|
||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"environment_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "ID of the environment where the vpn should be created", | ||
}, | ||
"vpc_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "Id of the VPC for the vpn", | ||
}, | ||
"certificate": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Computed: true, | ||
Description: "Certificate to use when using IKEV2 vpn type", | ||
}, | ||
"preshared_key": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Computed: true, | ||
Description: "Preshared key to use when using L2TP vpn type", | ||
}, | ||
"public_ip": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Computed: true, | ||
Description: "Public IP address associated with the vpn", | ||
}, | ||
"public_ip_id": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Computed: true, | ||
Description: "ID of the public IP address associated with the vpn", | ||
}, | ||
"state": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Computed: true, | ||
Description: "State of the vpn", | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Computed: true, | ||
Description: "Type of vpn connection", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceCloudcaVpnCreate(d *schema.ResourceData, meta interface{}) error { | ||
vpnIPPurpose := "SOURCE_NAT" | ||
ccaResources, rerr := getResourcesForEnvironmentID(meta.(*cca.CcaClient), d.Get("environment_id").(string)) | ||
if rerr != nil { | ||
return rerr | ||
} | ||
|
||
var vpnPubIPID string | ||
pubIps, _ := ccaResources.PublicIps.List() | ||
for _, ip := range pubIps { | ||
if ip.VpcId == d.Get("vpc_id").(string) { | ||
for _, purpose := range ip.Purposes { | ||
if purpose == vpnIPPurpose { | ||
vpnPubIPID = ip.Id | ||
break | ||
} | ||
} | ||
} | ||
if vpnPubIPID != "" { | ||
break | ||
} | ||
} | ||
|
||
if vpnPubIPID == "" { | ||
return fmt.Errorf("Error enabling the VPN because no Source NAT IP was found for the VPC") | ||
} | ||
|
||
_, err := ccaResources.RemoteAccessVpn.Enable(vpnPubIPID) | ||
if err != nil { | ||
return fmt.Errorf("Error enabling the VPN: %s", err) | ||
} | ||
d.SetId(vpnPubIPID) | ||
return resourceCloudcaVpnRead(d, meta) | ||
} | ||
|
||
func resourceCloudcaVpnRead(d *schema.ResourceData, meta interface{}) error { | ||
ccaResources, rerr := getResourcesForEnvironmentID(meta.(*cca.CcaClient), d.Get("environment_id").(string)) | ||
if rerr != nil { | ||
return rerr | ||
} | ||
|
||
vpn, err := ccaResources.RemoteAccessVpn.Get(d.Id()) | ||
if err != nil { | ||
return handleNotFoundError("VPN", false, err, d) | ||
} | ||
|
||
if vpn.State == "Disabled" { | ||
// If the VPN is disabled, it means the VPN is not active | ||
// so this entity is "missing" (at least as far as terraform is concerned). | ||
d.SetId("") | ||
return handleNotFoundError("VPN Disabled", false, err, d) | ||
} | ||
if err := d.Set("state", vpn.State); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
if err := d.Set("certificate", vpn.Certificate); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
if err := d.Set("preshared_key", vpn.PresharedKey); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
if err := d.Set("public_ip", vpn.PublicIpAddress); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
if err := d.Set("public_ip_id", vpn.PublicIpAddressId); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
if err := d.Set("type", vpn.Type); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
return nil | ||
} | ||
|
||
func resourceCloudcaVpnDelete(d *schema.ResourceData, meta interface{}) error { | ||
ccaResources, rerr := getResourcesForEnvironmentID(meta.(*cca.CcaClient), d.Get("environment_id").(string)) | ||
if rerr != nil { | ||
return rerr | ||
} | ||
if _, err := ccaResources.RemoteAccessVpn.Disable(d.Id()); err != nil { | ||
if ccaError, ok := err.(api.CcaErrorResponse); ok { | ||
if ccaError.StatusCode == 404 { | ||
log.Printf("VPN with id=%s no longer exists", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
return handleNotFoundError("VPN Delete", true, err, d) | ||
} | ||
return handleNotFoundError("VPN Delete", true, err, d) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package cloudca | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/cloud-ca/go-cloudca" | ||
"github.com/cloud-ca/go-cloudca/api" | ||
"github.com/cloud-ca/go-cloudca/services/cloudca" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceCloudcaVpnUser() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceCloudcaVpnUserCreate, | ||
Read: resourceCloudcaVpnUserRead, | ||
Delete: resourceCloudcaVpnUserDelete, | ||
|
||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"environment_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "ID of the environment where the vpn should be created", | ||
}, | ||
"username": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "Username of the VPN user", | ||
}, | ||
"password": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "Password of the VPN user", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceCloudcaVpnUserCreate(d *schema.ResourceData, meta interface{}) error { | ||
ccaResources, rerr := getResourcesForEnvironmentID(meta.(*cca.CcaClient), d.Get("environment_id").(string)) | ||
if rerr != nil { | ||
return rerr | ||
} | ||
|
||
remoteAccessVpnUser := cloudca.RemoteAccessVpnUser{ | ||
Username: d.Get("username").(string), | ||
Password: d.Get("password").(string), | ||
} | ||
_, err := ccaResources.RemoteAccessVpnUser.Create(remoteAccessVpnUser) | ||
if err != nil { | ||
return fmt.Errorf("Error adding VPN user: %s", err) | ||
} | ||
|
||
// TODO: When the CMC API actually returns the ID of the created user, use it. | ||
// Currently there is no way to do a 'Get' based on the username, and we don't have the ID, so | ||
// we have to list all users and then loop through to match the username in order to find the ID. | ||
vpnUsers, err := ccaResources.RemoteAccessVpnUser.List() | ||
if err != nil { | ||
return fmt.Errorf("Error getting the created VPN user ID: %s", err) | ||
} | ||
var userID string | ||
for _, user := range vpnUsers { | ||
if user.Username == d.Get("username").(string) { | ||
userID = user.Id | ||
break | ||
} | ||
} | ||
if userID != "" { | ||
d.SetId(userID) | ||
} else { | ||
return fmt.Errorf("Error finding the created VPN user ID: %s", err) | ||
} | ||
return resourceCloudcaVpnUserRead(d, meta) | ||
} | ||
|
||
func resourceCloudcaVpnUserRead(d *schema.ResourceData, meta interface{}) error { | ||
ccaResources, rerr := getResourcesForEnvironmentID(meta.(*cca.CcaClient), d.Get("environment_id").(string)) | ||
if rerr != nil { | ||
return rerr | ||
} | ||
|
||
// Get the user based on the ID | ||
vpnUser, err := ccaResources.RemoteAccessVpnUser.Get(d.Id()) | ||
if err != nil { | ||
d.SetId("") | ||
// If we return an error instead of nil, then if a VPN user is removed via the web UI | ||
// it will break the ability for terraform to plan or apply any changes, so terraform | ||
// will be in a broken state which can not be recovered from. | ||
return nil | ||
} | ||
|
||
if err := d.Set("username", vpnUser.Username); err != nil { | ||
return fmt.Errorf("Error reading Trigger: %s", err) | ||
} | ||
return nil | ||
} | ||
|
||
func resourceCloudcaVpnUserDelete(d *schema.ResourceData, meta interface{}) error { | ||
ccaResources, rerr := getResourcesForEnvironmentID(meta.(*cca.CcaClient), d.Get("environment_id").(string)) | ||
if rerr != nil { | ||
return rerr | ||
} | ||
remoteAccessVpnUser := cloudca.RemoteAccessVpnUser{ | ||
Id: d.Id(), | ||
Username: d.Get("username").(string), | ||
} | ||
if _, err := ccaResources.RemoteAccessVpnUser.Delete(remoteAccessVpnUser); err != nil { | ||
if ccaError, ok := err.(api.CcaErrorResponse); ok { | ||
if ccaError.StatusCode == 404 { | ||
log.Printf("VPN User with id=%s no longer exists", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
return handleNotFoundError("VPN User Delete", true, err, d) | ||
} | ||
return handleNotFoundError("VPN User Delete", true, err, d) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.