github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_vpc_peering_connection.go (about)

     1  package aws
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/ec2"
    12  	"github.com/hashicorp/errwrap"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsVpcPeeringConnection() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsVPCPeeringCreate,
    20  		Read:   resourceAwsVPCPeeringRead,
    21  		Update: resourceAwsVPCPeeringUpdate,
    22  		Delete: resourceAwsVPCPeeringDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: schema.ImportStatePassthrough,
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"peer_owner_id": {
    29  				Type:     schema.TypeString,
    30  				Optional: true,
    31  				ForceNew: true,
    32  				Computed: true,
    33  			},
    34  			"peer_vpc_id": {
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  			"vpc_id": {
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  			},
    44  			"auto_accept": {
    45  				Type:     schema.TypeBool,
    46  				Optional: true,
    47  			},
    48  			"accept_status": {
    49  				Type:     schema.TypeString,
    50  				Computed: true,
    51  			},
    52  			"accepter":  vpcPeeringConnectionOptionsSchema(),
    53  			"requester": vpcPeeringConnectionOptionsSchema(),
    54  			"tags":      tagsSchema(),
    55  		},
    56  	}
    57  }
    58  
    59  func resourceAwsVPCPeeringCreate(d *schema.ResourceData, meta interface{}) error {
    60  	conn := meta.(*AWSClient).ec2conn
    61  
    62  	// Create the vpc peering connection
    63  	createOpts := &ec2.CreateVpcPeeringConnectionInput{
    64  		PeerVpcId: aws.String(d.Get("peer_vpc_id").(string)),
    65  		VpcId:     aws.String(d.Get("vpc_id").(string)),
    66  	}
    67  
    68  	if v, ok := d.GetOk("peer_owner_id"); ok {
    69  		createOpts.PeerOwnerId = aws.String(v.(string))
    70  	}
    71  
    72  	log.Printf("[DEBUG] VPC Peering Create options: %#v", createOpts)
    73  
    74  	resp, err := conn.CreateVpcPeeringConnection(createOpts)
    75  	if err != nil {
    76  		return errwrap.Wrapf("Error creating VPC Peering Connection: {{err}}", err)
    77  	}
    78  
    79  	// Get the ID and store it
    80  	rt := resp.VpcPeeringConnection
    81  	d.SetId(*rt.VpcPeeringConnectionId)
    82  	log.Printf("[INFO] VPC Peering Connection ID: %s", d.Id())
    83  
    84  	// Wait for the vpc peering connection to become available
    85  	log.Printf("[DEBUG] Waiting for VPC Peering Connection (%s) to become available.", d.Id())
    86  	stateConf := &resource.StateChangeConf{
    87  		Pending: []string{"pending"},
    88  		Target:  []string{"pending-acceptance"},
    89  		Refresh: resourceAwsVPCPeeringConnectionStateRefreshFunc(conn, d.Id()),
    90  		Timeout: 1 * time.Minute,
    91  	}
    92  	if _, err := stateConf.WaitForState(); err != nil {
    93  		return errwrap.Wrapf(fmt.Sprintf(
    94  			"Error waiting for VPC Peering Connection (%s) to become available: {{err}}",
    95  			d.Id()), err)
    96  	}
    97  
    98  	return resourceAwsVPCPeeringUpdate(d, meta)
    99  }
   100  
   101  func resourceAwsVPCPeeringRead(d *schema.ResourceData, meta interface{}) error {
   102  	conn := meta.(*AWSClient).ec2conn
   103  	pcRaw, status, err := resourceAwsVPCPeeringConnectionStateRefreshFunc(conn, d.Id())()
   104  	// Allow a failed VPC Peering Connection to fallthrough,
   105  	// to allow rest of the logic below to do its work.
   106  	if err != nil && status != "failed" {
   107  		return err
   108  	}
   109  
   110  	if pcRaw == nil {
   111  		d.SetId("")
   112  		return nil
   113  	}
   114  
   115  	pc := pcRaw.(*ec2.VpcPeeringConnection)
   116  
   117  	// The failed status is a status that we can assume just means the
   118  	// connection is gone. Destruction isn't allowed, and it eventually
   119  	// just "falls off" the console. See GH-2322
   120  	if pc.Status != nil {
   121  		status := map[string]bool{
   122  			"deleted":  true,
   123  			"deleting": true,
   124  			"expired":  true,
   125  			"failed":   true,
   126  			"rejected": true,
   127  		}
   128  		if _, ok := status[*pc.Status.Code]; ok {
   129  			log.Printf("[DEBUG] VPC Peering Connection (%s) in state (%s), removing.",
   130  				d.Id(), *pc.Status.Code)
   131  			d.SetId("")
   132  			return nil
   133  		}
   134  	}
   135  	log.Printf("[DEBUG] VPC Peering Connection response: %#v", pc)
   136  
   137  	d.Set("accept_status", pc.Status.Code)
   138  	d.Set("peer_owner_id", pc.AccepterVpcInfo.OwnerId)
   139  	d.Set("peer_vpc_id", pc.AccepterVpcInfo.VpcId)
   140  	d.Set("vpc_id", pc.RequesterVpcInfo.VpcId)
   141  
   142  	// When the VPC Peering Connection is pending acceptance,
   143  	// the details about accepter and/or requester peering
   144  	// options would not be included in the response.
   145  	if pc.AccepterVpcInfo.PeeringOptions != nil {
   146  		err := d.Set("accepter", flattenPeeringOptions(pc.AccepterVpcInfo.PeeringOptions))
   147  		if err != nil {
   148  			return errwrap.Wrapf("Error setting VPC Peering Connection accepter information: {{err}}", err)
   149  		}
   150  	}
   151  
   152  	if pc.RequesterVpcInfo.PeeringOptions != nil {
   153  		err := d.Set("requester", flattenPeeringOptions(pc.RequesterVpcInfo.PeeringOptions))
   154  		if err != nil {
   155  			return errwrap.Wrapf("Error setting VPC Peering Connection requester information: {{err}}", err)
   156  		}
   157  	}
   158  
   159  	err = d.Set("tags", tagsToMap(pc.Tags))
   160  	if err != nil {
   161  		return errwrap.Wrapf("Error setting VPC Peering Connection tags: {{err}}", err)
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func resourceVPCPeeringConnectionAccept(conn *ec2.EC2, id string) (string, error) {
   168  	log.Printf("[INFO] Accept VPC Peering Connection with ID: %s", id)
   169  
   170  	req := &ec2.AcceptVpcPeeringConnectionInput{
   171  		VpcPeeringConnectionId: aws.String(id),
   172  	}
   173  
   174  	resp, err := conn.AcceptVpcPeeringConnection(req)
   175  	if err != nil {
   176  		return "", err
   177  	}
   178  	pc := resp.VpcPeeringConnection
   179  
   180  	return *pc.Status.Code, nil
   181  }
   182  
   183  func resourceVPCPeeringConnectionOptionsModify(d *schema.ResourceData, meta interface{}) error {
   184  	conn := meta.(*AWSClient).ec2conn
   185  
   186  	modifyOpts := &ec2.ModifyVpcPeeringConnectionOptionsInput{
   187  		VpcPeeringConnectionId: aws.String(d.Id()),
   188  	}
   189  
   190  	if v, ok := d.GetOk("accepter"); ok {
   191  		if s := v.(*schema.Set); len(s.List()) > 0 {
   192  			co := s.List()[0].(map[string]interface{})
   193  			modifyOpts.AccepterPeeringConnectionOptions = expandPeeringOptions(co)
   194  		}
   195  	}
   196  
   197  	if v, ok := d.GetOk("requester"); ok {
   198  		if s := v.(*schema.Set); len(s.List()) > 0 {
   199  			co := s.List()[0].(map[string]interface{})
   200  			modifyOpts.RequesterPeeringConnectionOptions = expandPeeringOptions(co)
   201  		}
   202  	}
   203  
   204  	log.Printf("[DEBUG] VPC Peering Connection modify options: %#v", modifyOpts)
   205  	if _, err := conn.ModifyVpcPeeringConnectionOptions(modifyOpts); err != nil {
   206  		return err
   207  	}
   208  
   209  	return nil
   210  }
   211  
   212  func resourceAwsVPCPeeringUpdate(d *schema.ResourceData, meta interface{}) error {
   213  	conn := meta.(*AWSClient).ec2conn
   214  
   215  	if err := setTags(conn, d); err != nil {
   216  		return err
   217  	} else {
   218  		d.SetPartial("tags")
   219  	}
   220  
   221  	pcRaw, _, err := resourceAwsVPCPeeringConnectionStateRefreshFunc(conn, d.Id())()
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	if pcRaw == nil {
   227  		d.SetId("")
   228  		return nil
   229  	}
   230  	pc := pcRaw.(*ec2.VpcPeeringConnection)
   231  
   232  	if _, ok := d.GetOk("auto_accept"); ok {
   233  		if pc.Status != nil && *pc.Status.Code == "pending-acceptance" {
   234  			status, err := resourceVPCPeeringConnectionAccept(conn, d.Id())
   235  			if err != nil {
   236  				return errwrap.Wrapf("Unable to accept VPC Peering Connection: {{err}}", err)
   237  			}
   238  			log.Printf("[DEBUG] VPC Peering Connection accept status: %s", status)
   239  		}
   240  	}
   241  
   242  	if d.HasChange("accepter") || d.HasChange("requester") {
   243  		_, ok := d.GetOk("auto_accept")
   244  		if !ok && pc.Status != nil && *pc.Status.Code != "active" {
   245  			return fmt.Errorf("Unable to modify peering options. The VPC Peering Connection "+
   246  				"%q is not active. Please set `auto_accept` attribute to `true`, "+
   247  				"or activate VPC Peering Connection manually.", d.Id())
   248  		}
   249  
   250  		if err := resourceVPCPeeringConnectionOptionsModify(d, meta); err != nil {
   251  			return errwrap.Wrapf("Error modifying VPC Peering Connection options: {{err}}", err)
   252  		}
   253  	}
   254  
   255  	return resourceAwsVPCPeeringRead(d, meta)
   256  }
   257  
   258  func resourceAwsVPCPeeringDelete(d *schema.ResourceData, meta interface{}) error {
   259  	conn := meta.(*AWSClient).ec2conn
   260  
   261  	_, err := conn.DeleteVpcPeeringConnection(
   262  		&ec2.DeleteVpcPeeringConnectionInput{
   263  			VpcPeeringConnectionId: aws.String(d.Id()),
   264  		})
   265  
   266  	return err
   267  }
   268  
   269  // resourceAwsVPCPeeringConnectionStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   270  // a VPCPeeringConnection.
   271  func resourceAwsVPCPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   272  	return func() (interface{}, string, error) {
   273  		resp, err := conn.DescribeVpcPeeringConnections(&ec2.DescribeVpcPeeringConnectionsInput{
   274  			VpcPeeringConnectionIds: []*string{aws.String(id)},
   275  		})
   276  		if err != nil {
   277  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcPeeringConnectionID.NotFound" {
   278  				resp = nil
   279  			} else {
   280  				log.Printf("Error reading VPC Peering Connection details: %s", err)
   281  				return nil, "error", err
   282  			}
   283  		}
   284  
   285  		if resp == nil {
   286  			// Sometimes AWS just has consistency issues and doesn't see
   287  			// our instance yet. Return an empty state.
   288  			return nil, "", nil
   289  		}
   290  
   291  		pc := resp.VpcPeeringConnections[0]
   292  
   293  		// A VPC Peering Connection can exist in a failed state due to
   294  		// incorrect VPC ID, account ID, or overlapping IP address range,
   295  		// thus we short circuit before the time out would occur.
   296  		if pc != nil && *pc.Status.Code == "failed" {
   297  			return nil, "failed", errors.New(*pc.Status.Message)
   298  		}
   299  
   300  		return pc, *pc.Status.Code, nil
   301  	}
   302  }
   303  
   304  func vpcPeeringConnectionOptionsSchema() *schema.Schema {
   305  	return &schema.Schema{
   306  		Type:     schema.TypeSet,
   307  		Optional: true,
   308  		Computed: true,
   309  		MaxItems: 1,
   310  		Elem: &schema.Resource{
   311  			Schema: map[string]*schema.Schema{
   312  				"allow_remote_vpc_dns_resolution": {
   313  					Type:     schema.TypeBool,
   314  					Optional: true,
   315  					Default:  false,
   316  				},
   317  				"allow_classic_link_to_remote_vpc": {
   318  					Type:     schema.TypeBool,
   319  					Optional: true,
   320  					Default:  false,
   321  				},
   322  				"allow_vpc_to_remote_classic_link": {
   323  					Type:     schema.TypeBool,
   324  					Optional: true,
   325  					Default:  false,
   326  				},
   327  			},
   328  		},
   329  	}
   330  }
   331  
   332  func flattenPeeringOptions(options *ec2.VpcPeeringConnectionOptionsDescription) (results []map[string]interface{}) {
   333  	m := make(map[string]interface{})
   334  
   335  	if options.AllowDnsResolutionFromRemoteVpc != nil {
   336  		m["allow_remote_vpc_dns_resolution"] = *options.AllowDnsResolutionFromRemoteVpc
   337  	}
   338  
   339  	if options.AllowEgressFromLocalClassicLinkToRemoteVpc != nil {
   340  		m["allow_classic_link_to_remote_vpc"] = *options.AllowEgressFromLocalClassicLinkToRemoteVpc
   341  	}
   342  
   343  	if options.AllowEgressFromLocalVpcToRemoteClassicLink != nil {
   344  		m["allow_vpc_to_remote_classic_link"] = *options.AllowEgressFromLocalVpcToRemoteClassicLink
   345  	}
   346  
   347  	results = append(results, m)
   348  	return
   349  }
   350  
   351  func expandPeeringOptions(m map[string]interface{}) *ec2.PeeringConnectionOptionsRequest {
   352  	r := &ec2.PeeringConnectionOptionsRequest{}
   353  
   354  	if v, ok := m["allow_remote_vpc_dns_resolution"]; ok {
   355  		r.AllowDnsResolutionFromRemoteVpc = aws.Bool(v.(bool))
   356  	}
   357  
   358  	if v, ok := m["allow_classic_link_to_remote_vpc"]; ok {
   359  		r.AllowEgressFromLocalClassicLinkToRemoteVpc = aws.Bool(v.(bool))
   360  	}
   361  
   362  	if v, ok := m["allow_vpc_to_remote_classic_link"]; ok {
   363  		r.AllowEgressFromLocalVpcToRemoteClassicLink = aws.Bool(v.(bool))
   364  	}
   365  
   366  	return r
   367  }