Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add vpc endpoint approval resource and docs #783

Merged
merged 1 commit into from
Dec 28, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
75 changes: 75 additions & 0 deletions docs/resources/vpcep_approval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
subcategory: "VPC Endpoint (VPCEP)"
---

# huaweicloud\_vpcep\_approval

Provides a resource to manage the VPC endpoint connections.

## Example Usage

```hcl
variable "service_vpc_id" {}
variable "vm_port" {}
variable "vpc_id" {}
variable "network_id" {}

resource "huaweicloud_vpcep_service" "demo" {
name = "demo-service"
server_type = "VM"
vpc_id = var.service_vpc_id
port_id = var.vm_port
approval = true

port_mapping {
service_port = 8080
terminal_port = 80
}
}

resource "huaweicloud_vpcep_endpoint" "demo" {
service_id = huaweicloud_vpcep_service.demo.id
vpc_id = var.vpc_id
network_id = var.network_id
enable_dns = true

lifecycle {
# enable_dns and ip_address are not assigned until connecting to the service
ignore_changes = [enable_dns, ip_address]
}
}

resource "huaweicloud_vpcep_approval" "approval" {
service_id = huaweicloud_vpcep_service.demo.id
endpoints = [huaweicloud_vpcep_endpoint.demo.id]
}
```

## Argument Reference

The following arguments are supported:

* `region` - (Optional, String, ForceNew) The region in which to obtain the VPC endpoint service.
If omitted, the provider-level region will be used. Changing this creates a new resource.

* `service_id` (Optional, String, ForceNew) - Specifies the ID of the VPC endpoint service. Changing this creates a new resource.

* `endpoints` (Optional, List) - Specifies the list of VPC endpoint IDs which accepted to connect to VPC endpoint service.
The VPC endpoints will be rejected when the resource was destroyed.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The unique ID in UUID format which equals to the ID of the VPC endpoint service.

* `connections` - An array of VPC endpoints connect to the VPC endpoint service. Structure is documented below.
- `endpoint_id` - The unique ID of the VPC endpoint.
- `marker_id` - The packet ID of the VPC endpoint.
- `domain_id` - The user's domain ID.
- `status` - The connection status of the VPC endpoint.

## Timeouts
This resource provides the following timeouts configuration options:
- `create` - Default is 10 minute.
- `delete` - Default is 3 minute.
1 change: 1 addition & 0 deletions huaweicloud/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ func Provider() terraform.ResourceProvider {
"huaweicloud_vpc_peering_connection_accepter": resourceVpcPeeringConnectionAccepterV2(),
"huaweicloud_vpc_route": ResourceVPCRouteV2(),
"huaweicloud_vpc_subnet": ResourceVpcSubnetV1(),
"huaweicloud_vpcep_approval": ResourceVPCEndpointApproval(),
"huaweicloud_vpcep_endpoint": ResourceVPCEndpoint(),
"huaweicloud_vpcep_service": ResourceVPCEndpointService(),
"huaweicloud_vpnaas_endpoint_group": resourceVpnEndpointGroupV2(),
Expand Down
212 changes: 212 additions & 0 deletions huaweicloud/resource_huaweicloud_vpcep_approval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package huaweicloud

import (
"fmt"
"log"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/huaweicloud/golangsdk"
"github.com/huaweicloud/golangsdk/openstack/vpcep/v1/services"
)

const (
actionReceive string = "receive"
actionReject string = "reject"
)

var approvalActionStatusMap = map[string]string{
actionReceive: "accepted",
actionReject: "rejected",
}

func ResourceVPCEndpointApproval() *schema.Resource {
return &schema.Resource{
Create: resourceVPCEndpointApprovalCreate,
Read: resourceVPCEndpointApprovalRead,
Update: resourceVPCEndpointApprovalUpdate,
Delete: resourceVPCEndpointApprovalDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(3 * time.Minute),
},

Schema: map[string]*schema.Schema{
"region": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"service_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"endpoints": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"connections": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"endpoint_id": {
Type: schema.TypeString,
Computed: true,
},
"marker_id": {
Type: schema.TypeInt,
Computed: true,
},
"domain_id": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}

func resourceVPCEndpointApprovalCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
vpcepClient, err := config.VPCEPClient(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating Huaweicloud VPC endpoint client: %s", err)
}

// check status of the VPC endpoint service
serviceID := d.Get("service_id").(string)
n, err := services.Get(vpcepClient, serviceID).Extract()
if err != nil {
return fmt.Errorf("Error retrieving VPC endpoint service %s: %s", serviceID, err)
}
if n.Status != "available" {
return fmt.Errorf("Error the status of VPC endpoint service is %s, expected to be available", n.Status)
}

raw := d.Get("endpoints").(*schema.Set).List()
err = doConnectionAction(d, vpcepClient, serviceID, actionReceive, raw)
if err != nil {
return fmt.Errorf("Error receiving connections to VPC endpoint service %s: %s", serviceID, err)
}

d.SetId(serviceID)
return resourceVPCEndpointApprovalRead(d, meta)
}

func resourceVPCEndpointApprovalRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
vpcepClient, err := config.VPCEPClient(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating Huaweicloud VPC endpoint client: %s", err)
}

serviceID := d.Get("service_id").(string)
if conns, err := flattenVPCEndpointConnections(vpcepClient, serviceID); err == nil {
d.Set("connections", conns)
}

return nil
}

func resourceVPCEndpointApprovalUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
vpcepClient, err := config.VPCEPClient(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating Huaweicloud VPC endpoint client: %s", err)
}

if d.HasChange("endpoints") {
old, new := d.GetChange("endpoints")
oldConnSet := old.(*schema.Set)
newConnSet := new.(*schema.Set)
received := newConnSet.Difference(oldConnSet)
rejected := oldConnSet.Difference(newConnSet)

serviceID := d.Get("service_id").(string)
err = doConnectionAction(d, vpcepClient, serviceID, actionReceive, received.List())
if err != nil {
return fmt.Errorf("Error receiving connections to VPC endpoint service %s: %s", serviceID, err)
}

err = doConnectionAction(d, vpcepClient, serviceID, actionReject, rejected.List())
if err != nil {
return fmt.Errorf("Error rejecting connections to VPC endpoint service %s: %s", serviceID, err)
}
}
return resourceVPCEndpointApprovalRead(d, meta)
}

func resourceVPCEndpointApprovalDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
vpcepClient, err := config.VPCEPClient(GetRegion(d, config))
if err != nil {
return fmt.Errorf("Error creating Huaweicloud VPC endpoint client: %s", err)
}

serviceID := d.Get("service_id").(string)
raw := d.Get("endpoints").(*schema.Set).List()
err = doConnectionAction(d, vpcepClient, serviceID, actionReject, raw)
if err != nil {
return fmt.Errorf("Error rejecting connections to VPC endpoint service %s: %s", serviceID, err)
}

d.SetId("")
return nil
}

func doConnectionAction(d *schema.ResourceData, client *golangsdk.ServiceClient, serviceID, action string, raw []interface{}) error {
if len(raw) == 0 {
return nil
}

if _, ok := approvalActionStatusMap[action]; !ok {
return fmt.Errorf("approval action(%s) is invalid, only support %s or %s", action, actionReceive, actionReject)
}

targetStatus := approvalActionStatusMap[action]
for _, v := range raw {
// Each request accepts or rejects only one VPC endpoint
epID := v.(string)
connOpts := services.ConnActionOpts{
Action: action,
Endpoints: []string{epID},
}

log.Printf("[DEBUG] %s to endpoint %s from VPC endpoint service %s", action, epID, serviceID)
if result := services.ConnAction(client, serviceID, connOpts); result.Err != nil {
return result.Err
}

log.Printf("[INFO] Waiting for VPC endpoint(%s) to become %s", epID, targetStatus)
stateConf := &resource.StateChangeConf{
Pending: []string{"creating", "pendingAcceptance"},
Target: []string{targetStatus},
Refresh: waitForVPCEndpointStatus(client, epID),
Timeout: d.Timeout(schema.TimeoutCreate),
Delay: 3 * time.Second,
MinTimeout: 3 * time.Second,
}

_, stateErr := stateConf.WaitForState()
if stateErr != nil {
return fmt.Errorf(
"Error waiting for VPC endpoint(%s) to become %s: %s",
epID, targetStatus, stateErr)
}
}

return nil
}
Loading