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

[WIP]Transfer Server Support VPC Endpoint Type #11751

Closed
wants to merge 3 commits into from
Closed
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
196 changes: 189 additions & 7 deletions aws/resource_aws_transfer_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func resourceAwsTransferServer() *schema.Resource {
Default: transfer.EndpointTypePublic,
ValidateFunc: validation.StringInSlice([]string{
transfer.EndpointTypePublic,
transfer.EndpointTypeVpc,
transfer.EndpointTypeVpcEndpoint,
}, false),
},
Expand All @@ -53,7 +54,7 @@ func resourceAwsTransferServer() *schema.Resource {
Schema: map[string]*schema.Schema{
"vpc_endpoint_id": {
Type: schema.TypeString,
Required: true,
Optional: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
validNamePattern := "^vpce-[0-9a-f]{17}$"
Expand All @@ -64,6 +65,28 @@ func resourceAwsTransferServer() *schema.Resource {
}
return
},
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if new == "" && d.Get("endpoint_type").(string) == transfer.EndpointTypeVpc {
return true
}
return false
},
},
"vpc_id": {
Type: schema.TypeString,
Optional: true,
},
"subnet_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"address_allocation_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
Expand Down Expand Up @@ -155,7 +178,7 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e
}

if attr, ok := d.GetOk("endpoint_details"); ok {
createOpts.EndpointDetails = expandTransferServerEndpointDetails(attr.([]interface{}))
createOpts.EndpointDetails = expandTransferServerEndpointDetails(attr.([]interface{}), false)
}

if attr, ok := d.GetOk("host_key"); ok {
Expand All @@ -171,6 +194,48 @@ func resourceAwsTransferServerCreate(d *schema.ResourceData, meta interface{}) e

d.SetId(*resp.ServerId)

if err := transferServerWaitForServerOnline(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("error waiting for Trasfer Server (%s) creation: %s", d.Id(), err)
}

if attr, ok := d.GetOk("endpoint_details"); ok {
ed := expandTransferServerEndpointDetails(attr.([]interface{}), true)
if len(ed.AddressAllocationIds) > 0 {
if err := stopTransferServer(d, conn); err != nil {
return err
}
if err := transferServerWaitForServerOffline(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("error waiting for Trasfer Server (%s) to stop: %s", d.Id(), err)
}
updateOpts := &transfer.UpdateServerInput{
ServerId: aws.String(d.Id()),
}
updateOpts.EndpointDetails = ed
err := resource.Retry(10*time.Minute, func() *resource.RetryError {
_, err := conn.UpdateServer(updateOpts)

if isAWSErr(err, transfer.ErrCodeConflictException, "VPC Endpoint state is not yet available") {
return nil
}

if err != nil {
return resource.NonRetryableError(err)
}

return resource.RetryableError(fmt.Errorf("Transfer Server (%s) in conflicted state", d.Id()))
})
if err != nil {
return fmt.Errorf("error updating Transfer Server (%s): %s", d.Id(), err)
}
if err := startTransferServer(d, conn); err != nil {
return err
}
if err := transferServerWaitForServerOnline(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("error waiting for Trasfer Server (%s) to start: %s", d.Id(), err)
}
}
}

return resourceAwsTransferServerRead(d, meta)
}

Expand All @@ -193,6 +258,7 @@ func resourceAwsTransferServerRead(d *schema.ResourceData, meta interface{}) err
}
return err
}
log.Printf("[DEBUG] Reading Transfer Server %#v", resp.Server)

endpoint := meta.(*AWSClient).RegionalHostname(fmt.Sprintf("%s.server.transfer", d.Id()))

Expand All @@ -219,6 +285,7 @@ func resourceAwsTransferServerRead(d *schema.ResourceData, meta interface{}) err
func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).transferconn
updateFlag := false
stopFlag := false
updateOpts := &transfer.UpdateServerInput{
ServerId: aws.String(d.Id()),
}
Expand Down Expand Up @@ -251,7 +318,10 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e
if d.HasChange("endpoint_details") {
updateFlag = true
if attr, ok := d.GetOk("endpoint_details"); ok {
updateOpts.EndpointDetails = expandTransferServerEndpointDetails(attr.([]interface{}))
if d.HasChange("endpoint_details.0.address_allocation_ids") {
stopFlag = true
}
updateOpts.EndpointDetails = expandTransferServerEndpointDetails(attr.([]interface{}), updateFlag)
}
}

Expand All @@ -263,6 +333,15 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e
}

if updateFlag {
if stopFlag {
if err := stopTransferServer(d, conn); err != nil {
return err
}
if err := transferServerWaitForServerOffline(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("error waiting for Trasfer Server (%s) to stop: %s", d.Id(), err)
}
}
log.Printf("[DEBUG] Updating Transfer Server %#v", updateOpts)
_, err := conn.UpdateServer(updateOpts)
if err != nil {
if isAWSErr(err, transfer.ErrCodeResourceNotFoundException, "") {
Expand All @@ -272,6 +351,14 @@ func resourceAwsTransferServerUpdate(d *schema.ResourceData, meta interface{}) e
}
return fmt.Errorf("error updating Transfer Server (%s): %s", d.Id(), err)
}
if stopFlag {
if err := startTransferServer(d, conn); err != nil {
return err
}
if err := transferServerWaitForServerOnline(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("error waiting for Trasfer Server (%s) to start: %s", d.Id(), err)
}
}
}

if d.HasChange("tags") {
Expand Down Expand Up @@ -386,15 +473,30 @@ func deleteTransferUsers(conn *transfer.Transfer, serverID string, nextToken *st
return nil
}

func expandTransferServerEndpointDetails(l []interface{}) *transfer.EndpointDetails {
func expandTransferServerEndpointDetails(l []interface{}, isUpdate bool) *transfer.EndpointDetails {
if len(l) == 0 || l[0] == nil {
return nil
}
e := l[0].(map[string]interface{})
ed := &transfer.EndpointDetails{}

if v, ok := e["vpc_endpoint_id"].(string); ok && v != "" {
ed.VpcEndpointId = aws.String(v)
}

return &transfer.EndpointDetails{
VpcEndpointId: aws.String(e["vpc_endpoint_id"].(string)),
if v, ok := e["vpc_id"].(string); ok && v != "" {
ed.VpcId = aws.String(v)
}

if v, ok := e["subnet_ids"].(*schema.Set); ok && v.Len() > 0 {
ed.SubnetIds = expandStringSet(v)
}

if v, ok := e["address_allocation_ids"].(*schema.Set); ok && v.Len() > 0 && isUpdate {
ed.AddressAllocationIds = expandStringSet(v)
}

return ed
}

func flattenTransferServerEndpointDetails(endpointDetails *transfer.EndpointDetails) []interface{} {
Expand All @@ -403,8 +505,88 @@ func flattenTransferServerEndpointDetails(endpointDetails *transfer.EndpointDeta
}

e := map[string]interface{}{
"vpc_endpoint_id": aws.StringValue(endpointDetails.VpcEndpointId),
"vpc_endpoint_id": aws.StringValue(endpointDetails.VpcEndpointId),
"vpc_id": aws.StringValue(endpointDetails.VpcId),
"subnet_ids": flattenStringSet(endpointDetails.SubnetIds),
"address_allocation_ids": flattenStringSet(endpointDetails.AddressAllocationIds),
}

return []interface{}{e}
}

func stopTransferServer(d *schema.ResourceData, conn *transfer.Transfer) error {
stopReq := &transfer.StopServerInput{
ServerId: aws.String(d.Id()),
}
if _, err := conn.StopServer(stopReq); err != nil {
return fmt.Errorf("error stopping Transfer Server (%s): %s", d.Id(), err)
}
return nil
}

func startTransferServer(d *schema.ResourceData, conn *transfer.Transfer) error {
stopReq := &transfer.StartServerInput{
ServerId: aws.String(d.Id()),
}
if _, err := conn.StartServer(stopReq); err != nil {
return fmt.Errorf("error starting Transfer Server (%s): %s", d.Id(), err)
}
return nil
}

func transferServerWaitForServerOnline(conn *transfer.Transfer, serverId string, timeout time.Duration) error {
stateChangeConf := &resource.StateChangeConf{
Pending: []string{transfer.StateStarting, transfer.StateOffline, transfer.StateStopping},
Target: []string{transfer.StateOnline},
Refresh: transferRefreshServerStatus(conn, serverId),
Timeout: timeout,
Delay: 10 * time.Second,
}

_, err := stateChangeConf.WaitForState()

return err
}

func transferServerWaitForServerOffline(conn *transfer.Transfer, serverId string, timeout time.Duration) error {
stateChangeConf := &resource.StateChangeConf{
Pending: []string{transfer.StateStarting, transfer.StateOnline, transfer.StateStopping},
Target: []string{transfer.StateOffline},
Refresh: transferRefreshServerStatus(conn, serverId),
Timeout: timeout,
Delay: 10 * time.Second,
}

_, err := stateChangeConf.WaitForState()

return err
}

func transferRefreshServerStatus(conn *transfer.Transfer, serverId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
server, err := describeTransferServer(conn, serverId)

if server == nil {
return 42, "destroyed", nil
}

if server.State != nil {
log.Printf("[DEBUG] Transfer Server status (%s): %s", serverId, *server.State)
}

return server, aws.StringValue(server.State), err
}
}

func describeTransferServer(conn *transfer.Transfer, serverId string) (*transfer.DescribedServer, error) {
params := &transfer.DescribeServerInput{
ServerId: aws.String(serverId),
}

resp, err := conn.DescribeServer(params)
if err != nil {
log.Printf("[WARN] Error on descibing Transfer Server: %s", err)
return nil, err
}
return resp.Server, err
}
Loading