github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/ec2/environ_vpc_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package ec2
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"gopkg.in/amz.v3/ec2"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	envtesting "github.com/juju/juju/environs/testing"
    16  	"github.com/juju/juju/network"
    17  )
    18  
    19  type vpcSuite struct {
    20  	testing.IsolationSuite
    21  
    22  	stubAPI *stubVPCAPIClient
    23  }
    24  
    25  var _ = gc.Suite(&vpcSuite{})
    26  
    27  func (s *vpcSuite) SetUpTest(c *gc.C) {
    28  	s.IsolationSuite.SetUpTest(c)
    29  
    30  	s.stubAPI = &stubVPCAPIClient{Stub: &testing.Stub{}}
    31  }
    32  
    33  func (s *vpcSuite) TestValidateBootstrapVPCUnexpectedError(c *gc.C) {
    34  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
    35  
    36  	err := validateBootstrapVPC(s.stubAPI, "region", anyVPCID, false, envtesting.BootstrapContext(c))
    37  	s.checkErrorMatchesCannotVerifyVPC(c, err)
    38  
    39  	s.stubAPI.CheckCallNames(c, "VPCs")
    40  }
    41  
    42  func (*vpcSuite) checkErrorMatchesCannotVerifyVPC(c *gc.C, err error) {
    43  	expectedError := `Juju could not verify whether the given vpc-id(.|\n)*AWS failed!`
    44  	c.Check(err, gc.ErrorMatches, expectedError)
    45  }
    46  
    47  func (s *vpcSuite) TestValidateModelVPCUnexpectedError(c *gc.C) {
    48  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
    49  
    50  	err := validateModelVPC(s.stubAPI, "model", anyVPCID)
    51  	s.checkErrorMatchesCannotVerifyVPC(c, err)
    52  
    53  	s.stubAPI.CheckCallNames(c, "VPCs")
    54  }
    55  
    56  func (s *vpcSuite) TestValidateModelVPCNotUsableError(c *gc.C) {
    57  	s.stubAPI.SetErrors(makeVPCNotFoundError("foo"))
    58  
    59  	err := validateModelVPC(s.stubAPI, "model", "foo")
    60  	expectedError := `Juju cannot use the given vpc-id for the model being added(.|\n)*vpc ID 'foo' does not exist.*`
    61  	c.Check(err, gc.ErrorMatches, expectedError)
    62  	c.Check(err, jc.Satisfies, isVPCNotUsableError)
    63  
    64  	s.stubAPI.CheckCallNames(c, "VPCs")
    65  }
    66  
    67  func (s *vpcSuite) TestValidateModelVPCIDNotSetOrNone(c *gc.C) {
    68  	const emptyVPCID = ""
    69  	err := validateModelVPC(s.stubAPI, "model", emptyVPCID)
    70  	c.Check(err, jc.ErrorIsNil)
    71  
    72  	err = validateModelVPC(s.stubAPI, "model", vpcIDNone)
    73  	c.Check(err, jc.ErrorIsNil)
    74  
    75  	s.stubAPI.CheckNoCalls(c)
    76  }
    77  
    78  // NOTE: validateVPC tests only verify expected error types for all code paths,
    79  // but do not check passed API arguments or exact error messages, as those are
    80  // extensively tested separately below.
    81  
    82  func (s *vpcSuite) TestValidateVPCWithEmptyVPCIDOrNilAPIClient(c *gc.C) {
    83  	err := validateVPC(s.stubAPI, "")
    84  	c.Assert(err, gc.ErrorMatches, "invalid arguments: empty VPC ID or nil client")
    85  
    86  	err = validateVPC(nil, anyVPCID)
    87  	c.Assert(err, gc.ErrorMatches, "invalid arguments: empty VPC ID or nil client")
    88  
    89  	s.stubAPI.CheckNoCalls(c)
    90  }
    91  
    92  func (s *vpcSuite) TestValidateVPCWhenVPCIDNotFound(c *gc.C) {
    93  	s.stubAPI.SetErrors(makeVPCNotFoundError("foo"))
    94  
    95  	err := validateVPC(s.stubAPI, anyVPCID)
    96  	c.Check(err, jc.Satisfies, isVPCNotUsableError)
    97  
    98  	s.stubAPI.CheckCallNames(c, "VPCs")
    99  }
   100  
   101  func (s *vpcSuite) TestValidateVPCWhenVPCHasNoSubnets(c *gc.C) {
   102  	s.stubAPI.SetVPCsResponse(1, availableState, notDefaultVPC)
   103  	s.stubAPI.SetSubnetsResponse(noResults, anyZone, noPublicIPOnLaunch)
   104  
   105  	err := validateVPC(s.stubAPI, anyVPCID)
   106  	c.Check(err, jc.Satisfies, isVPCNotUsableError)
   107  
   108  	s.stubAPI.CheckCallNames(c, "VPCs", "Subnets")
   109  }
   110  func (s *vpcSuite) TestValidateVPCWhenVPCNotAvailable(c *gc.C) {
   111  	s.stubAPI.PrepareValidateVPCResponses()
   112  	s.stubAPI.SetVPCsResponse(1, "bad-state", notDefaultVPC)
   113  
   114  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "VPCs")
   115  }
   116  
   117  func (s *vpcSuite) TestValidateVPCWhenVPCHasNoPublicSubnets(c *gc.C) {
   118  	s.stubAPI.PrepareValidateVPCResponses()
   119  	s.stubAPI.SetSubnetsResponse(1, anyZone, noPublicIPOnLaunch)
   120  
   121  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "Subnets")
   122  }
   123  
   124  func (s *vpcSuite) TestValidateVPCWhenVPCHasNoGateway(c *gc.C) {
   125  	s.stubAPI.PrepareValidateVPCResponses()
   126  	s.stubAPI.SetGatewaysResponse(noResults, anyState)
   127  
   128  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "InternetGateways")
   129  }
   130  
   131  func (s *vpcSuite) TestValidateVPCWhenVPCHasNoAttachedGateway(c *gc.C) {
   132  	s.stubAPI.PrepareValidateVPCResponses()
   133  	s.stubAPI.SetGatewaysResponse(1, "pending")
   134  
   135  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "InternetGateways")
   136  }
   137  
   138  func (s *vpcSuite) TestValidateVPCWhenVPCHasNoRouteTables(c *gc.C) {
   139  	s.stubAPI.PrepareValidateVPCResponses()
   140  	s.stubAPI.SetRouteTablesResponse() // no route tables at all
   141  
   142  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "RouteTables")
   143  }
   144  
   145  func (s *vpcSuite) TestValidateVPCWhenVPCHasNoMainRouteTable(c *gc.C) {
   146  	s.stubAPI.PrepareValidateVPCResponses()
   147  	s.stubAPI.SetRouteTablesResponse(
   148  		makeEC2RouteTable(anyTableID, notMainRouteTable, nil, nil),
   149  	)
   150  
   151  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "RouteTables")
   152  }
   153  
   154  func (s *vpcSuite) TestValidateVPCWhenVPCHasMainRouteTableWithoutRoutes(c *gc.C) {
   155  	s.stubAPI.PrepareValidateVPCResponses()
   156  	s.stubAPI.SetRouteTablesResponse(
   157  		makeEC2RouteTable(anyTableID, mainRouteTable, nil, nil),
   158  	)
   159  
   160  	s.stubAPI.CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c, "RouteTables")
   161  }
   162  
   163  func (s *vpcSuite) TestValidateVPCSuccess(c *gc.C) {
   164  	s.stubAPI.PrepareValidateVPCResponses()
   165  
   166  	err := validateVPC(s.stubAPI, anyVPCID)
   167  	c.Assert(err, jc.ErrorIsNil)
   168  
   169  	s.stubAPI.CheckCallNames(c, "VPCs", "Subnets", "InternetGateways", "RouteTables")
   170  }
   171  
   172  func (s *vpcSuite) TestValidateModelVPCSuccess(c *gc.C) {
   173  	s.stubAPI.PrepareValidateVPCResponses()
   174  
   175  	err := validateModelVPC(s.stubAPI, "model", anyVPCID)
   176  	c.Assert(err, jc.ErrorIsNil)
   177  
   178  	s.stubAPI.CheckCallNames(c, "VPCs", "Subnets", "InternetGateways", "RouteTables")
   179  	c.Check(c.GetTestLog(), jc.Contains, `INFO juju.provider.ec2 Using VPC "vpc-anything" for model "model"`)
   180  }
   181  
   182  func (s *vpcSuite) TestValidateModelVPCNotRecommendedStillOK(c *gc.C) {
   183  	s.stubAPI.PrepareValidateVPCResponses()
   184  	s.stubAPI.SetSubnetsResponse(1, anyZone, noPublicIPOnLaunch)
   185  
   186  	err := validateModelVPC(s.stubAPI, "model", anyVPCID)
   187  	c.Assert(err, jc.ErrorIsNil)
   188  
   189  	s.stubAPI.CheckCallNames(c, "VPCs", "Subnets")
   190  	testLog := c.GetTestLog()
   191  	c.Check(testLog, jc.Contains, `INFO juju.provider.ec2 Juju will use, but does not recommend `+
   192  		`using VPC "vpc-anything": VPC contains no public subnets`)
   193  	c.Check(testLog, jc.Contains, `INFO juju.provider.ec2 Using VPC "vpc-anything" for model "model"`)
   194  }
   195  
   196  func (s *vpcSuite) TestGetVPCByIDWithMissingID(c *gc.C) {
   197  	s.stubAPI.SetErrors(makeVPCNotFoundError("foo"))
   198  
   199  	vpc, err := getVPCByID(s.stubAPI, "foo")
   200  	c.Assert(err, gc.ErrorMatches, `The vpc ID 'foo' does not exist \(InvalidVpcID.NotFound\)`)
   201  	c.Check(err, jc.Satisfies, isVPCNotUsableError)
   202  	c.Check(vpc, gc.IsNil)
   203  
   204  	s.stubAPI.CheckSingleVPCsCall(c, "foo")
   205  }
   206  
   207  func (s *vpcSuite) TestGetVPCByIDUnexpectedAWSError(c *gc.C) {
   208  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
   209  
   210  	vpc, err := getVPCByID(s.stubAPI, "bar")
   211  	c.Assert(err, gc.ErrorMatches, `unexpected AWS response getting VPC "bar": AWS failed!`)
   212  	c.Check(vpc, gc.IsNil)
   213  
   214  	s.stubAPI.CheckSingleVPCsCall(c, "bar")
   215  }
   216  
   217  func (s *vpcSuite) TestGetVPCByIDNoResults(c *gc.C) {
   218  	s.stubAPI.SetVPCsResponse(noResults, anyState, notDefaultVPC)
   219  
   220  	vpc, err := getVPCByID(s.stubAPI, "vpc-42")
   221  	c.Assert(err, gc.ErrorMatches, `VPC "vpc-42" not found`)
   222  	c.Check(err, jc.Satisfies, isVPCNotUsableError)
   223  	c.Check(vpc, gc.IsNil)
   224  
   225  	s.stubAPI.CheckSingleVPCsCall(c, "vpc-42")
   226  }
   227  
   228  func (s *vpcSuite) TestGetVPCByIDMultipleResults(c *gc.C) {
   229  	s.stubAPI.SetVPCsResponse(5, anyState, notDefaultVPC)
   230  
   231  	vpc, err := getVPCByID(s.stubAPI, "vpc-33")
   232  	c.Assert(err, gc.ErrorMatches, "expected 1 result from AWS, got 5")
   233  	c.Check(vpc, gc.IsNil)
   234  
   235  	s.stubAPI.CheckSingleVPCsCall(c, "vpc-33")
   236  }
   237  
   238  func (s *vpcSuite) TestGetVPCByIDSuccess(c *gc.C) {
   239  	s.stubAPI.SetVPCsResponse(1, anyState, notDefaultVPC)
   240  
   241  	vpc, err := getVPCByID(s.stubAPI, "vpc-1")
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	c.Check(vpc, jc.DeepEquals, &s.stubAPI.vpcsResponse.VPCs[0])
   244  
   245  	s.stubAPI.CheckSingleVPCsCall(c, "vpc-1")
   246  }
   247  
   248  func (s *vpcSuite) TestIsVPCNotFoundError(c *gc.C) {
   249  	c.Check(isVPCNotFoundError(nil), jc.IsFalse)
   250  
   251  	nonEC2Error := errors.New("boom")
   252  	c.Check(isVPCNotFoundError(nonEC2Error), jc.IsFalse)
   253  
   254  	ec2Error := makeEC2Error(444, "code", "bad stuff", "req-id")
   255  	c.Check(isVPCNotFoundError(ec2Error), jc.IsFalse)
   256  
   257  	ec2Error = makeVPCNotFoundError("some-id")
   258  	c.Check(isVPCNotFoundError(ec2Error), jc.IsTrue)
   259  }
   260  
   261  func (s *vpcSuite) TestCheckVPCIsAvailable(c *gc.C) {
   262  	availableVPC := makeEC2VPC(anyVPCID, availableState)
   263  	c.Check(checkVPCIsAvailable(availableVPC), jc.ErrorIsNil)
   264  
   265  	defaultVPC := makeEC2VPC(anyVPCID, availableState)
   266  	defaultVPC.IsDefault = true
   267  	c.Check(checkVPCIsAvailable(defaultVPC), jc.ErrorIsNil)
   268  
   269  	notAvailableVPC := makeEC2VPC(anyVPCID, anyState)
   270  	err := checkVPCIsAvailable(notAvailableVPC)
   271  	c.Assert(err, gc.ErrorMatches, `VPC has unexpected state "any state"`)
   272  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   273  }
   274  
   275  func (s *vpcSuite) TestGetVPCSubnetUnexpectedAWSError(c *gc.C) {
   276  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
   277  
   278  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   279  	subnets, err := getVPCSubnets(s.stubAPI, anyVPC)
   280  	c.Assert(err, gc.ErrorMatches, `unexpected AWS response getting subnets of VPC "vpc-anything": AWS failed!`)
   281  	c.Check(subnets, gc.IsNil)
   282  
   283  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   284  }
   285  
   286  func (s *vpcSuite) TestGetVPCSubnetsNoResults(c *gc.C) {
   287  	s.stubAPI.SetSubnetsResponse(noResults, anyZone, noPublicIPOnLaunch)
   288  
   289  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   290  	subnets, err := getVPCSubnets(s.stubAPI, anyVPC)
   291  	c.Assert(err, gc.ErrorMatches, `no subnets found for VPC "vpc-anything"`)
   292  	c.Check(err, jc.Satisfies, isVPCNotUsableError)
   293  	c.Check(subnets, gc.IsNil)
   294  
   295  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   296  }
   297  
   298  func (s *vpcSuite) TestGetVPCSubnetsSuccess(c *gc.C) {
   299  	s.stubAPI.SetSubnetsResponse(3, anyZone, noPublicIPOnLaunch)
   300  
   301  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   302  	subnets, err := getVPCSubnets(s.stubAPI, anyVPC)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Check(subnets, jc.DeepEquals, s.stubAPI.subnetsResponse.Subnets)
   305  
   306  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   307  }
   308  
   309  func (s *vpcSuite) TestFindFirstPublicSubnetSuccess(c *gc.C) {
   310  	s.stubAPI.SetSubnetsResponse(3, anyZone, withPublicIPOnLaunch)
   311  	s.stubAPI.subnetsResponse.Subnets[0].MapPublicIPOnLaunch = false
   312  
   313  	subnet, err := findFirstPublicSubnet(s.stubAPI.subnetsResponse.Subnets)
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	c.Check(subnet, jc.DeepEquals, &s.stubAPI.subnetsResponse.Subnets[1])
   316  }
   317  
   318  func (s *vpcSuite) TestFindFirstPublicSubnetNoneFound(c *gc.C) {
   319  	s.stubAPI.SetSubnetsResponse(3, anyZone, noPublicIPOnLaunch)
   320  
   321  	subnet, err := findFirstPublicSubnet(s.stubAPI.subnetsResponse.Subnets)
   322  	c.Assert(err, gc.ErrorMatches, "VPC contains no public subnets")
   323  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   324  	c.Check(subnet, gc.IsNil)
   325  }
   326  
   327  func (s *vpcSuite) TestGetVPCInternetGatewayNoResults(c *gc.C) {
   328  	s.stubAPI.SetGatewaysResponse(noResults, anyState)
   329  
   330  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   331  	gateway, err := getVPCInternetGateway(s.stubAPI, anyVPC)
   332  	c.Assert(err, gc.ErrorMatches, `VPC has no Internet Gateway attached`)
   333  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   334  	c.Check(gateway, gc.IsNil)
   335  
   336  	s.stubAPI.CheckSingleInternetGatewaysCall(c, anyVPC)
   337  }
   338  
   339  func (s *vpcSuite) TestGetVPCInternetGatewayUnexpectedAWSError(c *gc.C) {
   340  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
   341  
   342  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   343  	gateway, err := getVPCInternetGateway(s.stubAPI, anyVPC)
   344  	c.Assert(err, gc.ErrorMatches, `unexpected AWS response getting Internet Gateway of VPC "vpc-anything": AWS failed!`)
   345  	c.Check(gateway, gc.IsNil)
   346  
   347  	s.stubAPI.CheckSingleInternetGatewaysCall(c, anyVPC)
   348  }
   349  
   350  func (s *vpcSuite) TestGetVPCInternetGatewayMultipleResults(c *gc.C) {
   351  	s.stubAPI.SetGatewaysResponse(3, anyState)
   352  
   353  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   354  	gateway, err := getVPCInternetGateway(s.stubAPI, anyVPC)
   355  	c.Assert(err, gc.ErrorMatches, "expected 1 result from AWS, got 3")
   356  	c.Check(gateway, gc.IsNil)
   357  
   358  	s.stubAPI.CheckSingleInternetGatewaysCall(c, anyVPC)
   359  }
   360  
   361  func (s *vpcSuite) TestGetVPCInternetGatewaySuccess(c *gc.C) {
   362  	s.stubAPI.SetGatewaysResponse(1, anyState)
   363  
   364  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   365  	gateway, err := getVPCInternetGateway(s.stubAPI, anyVPC)
   366  	c.Assert(err, jc.ErrorIsNil)
   367  	c.Check(gateway, jc.DeepEquals, &s.stubAPI.gatewaysResponse.InternetGateways[0])
   368  
   369  	s.stubAPI.CheckSingleInternetGatewaysCall(c, anyVPC)
   370  }
   371  
   372  func (s *vpcSuite) TestCheckInternetGatewayIsAvailable(c *gc.C) {
   373  	availableIGW := makeEC2InternetGateway(anyGatewayID, availableState)
   374  	c.Check(checkInternetGatewayIsAvailable(availableIGW), jc.ErrorIsNil)
   375  
   376  	pendingIGW := makeEC2InternetGateway(anyGatewayID, "pending")
   377  	err := checkInternetGatewayIsAvailable(pendingIGW)
   378  	c.Assert(err, gc.ErrorMatches, `VPC has Internet Gateway "igw-anything" in unexpected state "pending"`)
   379  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   380  }
   381  
   382  func (s *vpcSuite) TestGetVPCRouteTablesNoResults(c *gc.C) {
   383  	s.stubAPI.SetRouteTablesResponse() // no results
   384  
   385  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   386  	tables, err := getVPCRouteTables(s.stubAPI, anyVPC)
   387  	c.Assert(err, gc.ErrorMatches, `VPC has no route tables`)
   388  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   389  	c.Check(tables, gc.IsNil)
   390  
   391  	s.stubAPI.CheckSingleRouteTablesCall(c, anyVPC)
   392  }
   393  
   394  func (s *vpcSuite) TestGetVPCRouteTablesUnexpectedAWSError(c *gc.C) {
   395  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
   396  
   397  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   398  	tables, err := getVPCRouteTables(s.stubAPI, anyVPC)
   399  	c.Assert(err, gc.ErrorMatches, `unexpected AWS response getting route tables of VPC "vpc-anything": AWS failed!`)
   400  	c.Check(tables, gc.IsNil)
   401  
   402  	s.stubAPI.CheckSingleRouteTablesCall(c, anyVPC)
   403  }
   404  
   405  func (s *vpcSuite) TestGetVPCRouteTablesSuccess(c *gc.C) {
   406  	givenVPC := makeEC2VPC("vpc-given", anyState)
   407  	givenVPC.CIDRBlock = "0.1.0.0/16"
   408  	givenGateway := makeEC2InternetGateway("igw-given", availableState)
   409  
   410  	s.stubAPI.SetRouteTablesResponse(
   411  		makeEC2RouteTable("rtb-other", notMainRouteTable, []string{"subnet-1", "subnet-2"}, nil),
   412  		makeEC2RouteTable("rtb-main", mainRouteTable, nil, makeEC2Routes(
   413  			givenGateway.Id, givenVPC.CIDRBlock, activeState, 3, // 3 extra routes
   414  		)),
   415  	)
   416  
   417  	tables, err := getVPCRouteTables(s.stubAPI, givenVPC)
   418  	c.Assert(err, jc.ErrorIsNil)
   419  	c.Check(tables, jc.DeepEquals, s.stubAPI.routeTablesResponse.Tables)
   420  
   421  	s.stubAPI.CheckSingleRouteTablesCall(c, givenVPC)
   422  }
   423  
   424  func (s *vpcSuite) TestFindVPCMainRouteTableWithMainAndPerSubnetTables(c *gc.C) {
   425  	givenTables := []ec2.RouteTable{
   426  		*makeEC2RouteTable("rtb-main", mainRouteTable, nil, nil),
   427  		*makeEC2RouteTable("rtb-2-subnets", notMainRouteTable, []string{"subnet-1", "subnet-2"}, nil),
   428  	}
   429  
   430  	mainTable, err := findVPCMainRouteTable(givenTables)
   431  	c.Assert(err, gc.ErrorMatches, `subnet "subnet-1" not associated with VPC "vpc-anything" main route table`)
   432  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   433  	c.Check(mainTable, gc.IsNil)
   434  }
   435  
   436  func (s *vpcSuite) TestFindVPCMainRouteTableWithOnlyNonAssociatedTables(c *gc.C) {
   437  	givenTables := []ec2.RouteTable{
   438  		*makeEC2RouteTable("rtb-1", notMainRouteTable, nil, nil),
   439  		*makeEC2RouteTable("rtb-2", notMainRouteTable, nil, nil),
   440  		*makeEC2RouteTable("rtb-3", notMainRouteTable, nil, nil),
   441  	}
   442  
   443  	mainTable, err := findVPCMainRouteTable(givenTables)
   444  	c.Assert(err, gc.ErrorMatches, "VPC has no associated main route table")
   445  	c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   446  	c.Check(mainTable, gc.IsNil)
   447  }
   448  
   449  func (s *vpcSuite) TestFindVPCMainRouteTableWithSingleMainTable(c *gc.C) {
   450  	givenTables := []ec2.RouteTable{
   451  		*makeEC2RouteTable("rtb-main", mainRouteTable, nil, nil),
   452  	}
   453  
   454  	mainTable, err := findVPCMainRouteTable(givenTables)
   455  	c.Assert(err, jc.ErrorIsNil)
   456  	c.Check(mainTable, jc.DeepEquals, &givenTables[0])
   457  }
   458  
   459  func (s *vpcSuite) TestFindVPCMainRouteTableWithExtraMainTables(c *gc.C) {
   460  	givenTables := []ec2.RouteTable{
   461  		*makeEC2RouteTable("rtb-non-associated", notMainRouteTable, nil, nil),
   462  		*makeEC2RouteTable("rtb-main", mainRouteTable, nil, nil),
   463  		*makeEC2RouteTable("rtb-main-extra", mainRouteTable, nil, nil),
   464  	}
   465  
   466  	mainTable, err := findVPCMainRouteTable(givenTables)
   467  	c.Assert(err, jc.ErrorIsNil)
   468  	c.Check(mainTable, jc.DeepEquals, &givenTables[1]) // first found counts
   469  }
   470  
   471  func (s *vpcSuite) TestCheckVPCRouteTableRoutesWithNoDefaultRoute(c *gc.C) {
   472  	vpc, table, gateway := prepareCheckVPCRouteTableRoutesArgs()
   473  	c.Check(table.Routes, gc.HasLen, 0) // no routes at all
   474  
   475  	checkFailed := func() {
   476  		err := checkVPCRouteTableRoutes(vpc, table, gateway)
   477  		c.Assert(err, gc.ErrorMatches, `missing default route via gateway "igw-anything"`)
   478  		c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   479  	}
   480  	checkFailed()
   481  
   482  	table.Routes = makeEC2Routes(gateway.Id, vpc.CIDRBlock, "blackhole", 3) // inactive routes only
   483  	checkFailed()
   484  
   485  	table.Routes = makeEC2Routes("", vpc.CIDRBlock, activeState, 1) // local and 1 extra route
   486  	checkFailed()
   487  
   488  	table.Routes = makeEC2Routes("", vpc.CIDRBlock, activeState, 0) // local route only
   489  	checkFailed()
   490  }
   491  
   492  func (s *vpcSuite) TestCheckVPCRouteTableRoutesWithDefaultButNoLocalRoutes(c *gc.C) {
   493  	vpc, table, gateway := prepareCheckVPCRouteTableRoutesArgs()
   494  	table.Routes = makeEC2Routes(gateway.Id, "", activeState, 3) // default and 3 extra routes; no local route
   495  
   496  	checkFailed := func() {
   497  		err := checkVPCRouteTableRoutes(vpc, table, gateway)
   498  		c.Assert(err, gc.ErrorMatches, `missing local route with destination "0.1.0.0/16"`)
   499  		c.Check(err, jc.Satisfies, isVPCNotRecommendedError)
   500  	}
   501  	checkFailed()
   502  
   503  	table.Routes = makeEC2Routes(gateway.Id, "", activeState, 0) // only default route
   504  	checkFailed()
   505  }
   506  
   507  func (s *vpcSuite) TestCheckVPCRouteTableRoutesSuccess(c *gc.C) {
   508  	vpc, table, gateway := prepareCheckVPCRouteTableRoutesArgs()
   509  	table.Routes = makeEC2Routes(gateway.Id, vpc.CIDRBlock, activeState, 3) // default, local and 3 extra routes
   510  
   511  	err := checkVPCRouteTableRoutes(vpc, table, gateway)
   512  	c.Assert(err, jc.ErrorIsNil)
   513  }
   514  
   515  func (s *vpcSuite) TestFindDefaultVPCIDUnexpectedAWSError(c *gc.C) {
   516  	s.stubAPI.SetErrors(errors.New("AWS failed!"))
   517  
   518  	vpcID, err := findDefaultVPCID(s.stubAPI)
   519  	c.Assert(err, gc.ErrorMatches, "unexpected AWS response getting default-vpc account attribute: AWS failed!")
   520  	c.Check(vpcID, gc.Equals, "")
   521  
   522  	s.stubAPI.CheckSingleAccountAttributesCall(c, "default-vpc")
   523  }
   524  
   525  func (s *vpcSuite) TestFindDefaultVPCIDNoAttributeOrNoValue(c *gc.C) {
   526  	s.stubAPI.SetAttributesResponse(nil) // no attributes at all
   527  
   528  	checkFailed := func() {
   529  		vpcID, err := findDefaultVPCID(s.stubAPI)
   530  		c.Assert(err, gc.ErrorMatches, "default-vpc account attribute not found")
   531  		c.Check(err, jc.Satisfies, errors.IsNotFound)
   532  		c.Check(vpcID, gc.Equals, "")
   533  
   534  		s.stubAPI.CheckSingleAccountAttributesCall(c, "default-vpc")
   535  	}
   536  	checkFailed()
   537  
   538  	s.stubAPI.SetAttributesResponse(map[string][]string{
   539  		"any-attribute": nil, // no values
   540  	})
   541  	checkFailed()
   542  
   543  	s.stubAPI.SetAttributesResponse(map[string][]string{
   544  		"not-default-vpc-attribute": []string{"foo", "bar"}, // wrong name
   545  	})
   546  	checkFailed()
   547  
   548  	s.stubAPI.SetAttributesResponse(map[string][]string{
   549  		"default-vpc": nil, // name ok, no values
   550  	})
   551  	checkFailed()
   552  
   553  	s.stubAPI.SetAttributesResponse(map[string][]string{
   554  		"default-vpc": []string{}, // name ok, empty values
   555  	})
   556  	checkFailed()
   557  }
   558  
   559  func (s *vpcSuite) TestFindDefaultVPCIDWithExplicitNoneValue(c *gc.C) {
   560  	s.stubAPI.SetAttributesResponse(map[string][]string{
   561  		"default-vpc": []string{"none"},
   562  	})
   563  
   564  	vpcID, err := findDefaultVPCID(s.stubAPI)
   565  	c.Assert(err, gc.ErrorMatches, "default VPC not found")
   566  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   567  	c.Check(vpcID, gc.Equals, "")
   568  
   569  	s.stubAPI.CheckSingleAccountAttributesCall(c, "default-vpc")
   570  }
   571  
   572  func (s *vpcSuite) TestFindDefaultVPCIDSuccess(c *gc.C) {
   573  	s.stubAPI.SetAttributesResponse(map[string][]string{
   574  		"default-vpc": []string{"vpc-foo", "vpc-bar"},
   575  	})
   576  
   577  	vpcID, err := findDefaultVPCID(s.stubAPI)
   578  	c.Assert(err, jc.ErrorIsNil)
   579  	c.Check(vpcID, gc.Equals, "vpc-foo") // always the first value is used.
   580  
   581  	s.stubAPI.CheckSingleAccountAttributesCall(c, "default-vpc")
   582  }
   583  
   584  func (s *vpcSuite) TestGetVPCSubnetIDsForAvailabilityZoneWithSubnetsError(c *gc.C) {
   585  	s.stubAPI.SetErrors(errors.New("too cloudy"))
   586  
   587  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   588  	subnetIDs, err := getVPCSubnetIDsForAvailabilityZone(s.stubAPI, anyVPC.Id, anyZone, nil)
   589  	c.Assert(err, gc.ErrorMatches, `cannot get VPC "vpc-anything" subnets: unexpected AWS .*: too cloudy`)
   590  	c.Check(subnetIDs, gc.IsNil)
   591  
   592  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   593  }
   594  
   595  func (s *vpcSuite) TestGetVPCSubnetIDsForAvailabilityZoneNoSubnetsAtAll(c *gc.C) {
   596  	s.stubAPI.SetSubnetsResponse(noResults, anyZone, noPublicIPOnLaunch)
   597  
   598  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   599  	subnetIDs, err := getVPCSubnetIDsForAvailabilityZone(s.stubAPI, anyVPC.Id, anyZone, nil)
   600  	c.Assert(err, gc.ErrorMatches, `VPC "vpc-anything" has no subnets in AZ "any-zone": no subnets found for VPC.*`)
   601  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   602  	c.Check(subnetIDs, gc.IsNil)
   603  
   604  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   605  }
   606  
   607  func (s *vpcSuite) TestGetVPCSubnetIDsForAvailabilityZoneNoSubnetsInAZ(c *gc.C) {
   608  	s.stubAPI.SetSubnetsResponse(3, "other-zone", noPublicIPOnLaunch)
   609  
   610  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   611  	subnetIDs, err := getVPCSubnetIDsForAvailabilityZone(s.stubAPI, anyVPC.Id, "given-zone", nil)
   612  	c.Assert(err, gc.ErrorMatches, `VPC "vpc-anything" has no subnets in AZ "given-zone"`)
   613  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   614  	c.Check(subnetIDs, gc.IsNil)
   615  
   616  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   617  }
   618  
   619  func (s *vpcSuite) TestGetVPCSubnetIDsForAvailabilityZoneWithSubnetsToZones(c *gc.C) {
   620  	s.stubAPI.SetSubnetsResponse(4, "my-zone", noPublicIPOnLaunch)
   621  	// Simulate we used --constraints spaces=foo, which contains subnet-1 and
   622  	// subnet-3 out of the 4 subnets in AZ my-zone (see the related bug
   623  	// http://pad.lv/1609343).
   624  	allowedSubnetIDs := []string{"subnet-1", "subnet-3"}
   625  
   626  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   627  	subnetIDs, err := getVPCSubnetIDsForAvailabilityZone(s.stubAPI, anyVPC.Id, "my-zone", allowedSubnetIDs)
   628  	c.Assert(err, jc.ErrorIsNil)
   629  	c.Check(subnetIDs, jc.DeepEquals, []string{"subnet-1", "subnet-3"})
   630  
   631  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   632  }
   633  
   634  func (s *vpcSuite) TestGetVPCSubnetIDsForAvailabilityZoneSuccess(c *gc.C) {
   635  	s.stubAPI.SetSubnetsResponse(2, "my-zone", noPublicIPOnLaunch)
   636  
   637  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   638  	subnetIDs, err := getVPCSubnetIDsForAvailabilityZone(s.stubAPI, anyVPC.Id, "my-zone", nil)
   639  	c.Assert(err, jc.ErrorIsNil)
   640  	// Result slice of IDs is always sorted.
   641  	c.Check(subnetIDs, jc.DeepEquals, []string{"subnet-0", "subnet-1"})
   642  
   643  	s.stubAPI.CheckSingleSubnetsCall(c, anyVPC)
   644  }
   645  
   646  var fakeSubnetsToZones = map[network.Id][]string{
   647  	"subnet-foo": []string{"az1", "az2"},
   648  	"subnet-bar": []string{"az1"},
   649  	"subnet-oof": []string{"az3"},
   650  }
   651  
   652  func (s *vpcSuite) TestFindSubnetIDsForAvailabilityZoneNoneFound(c *gc.C) {
   653  	subnetIDs, err := findSubnetIDsForAvailabilityZone("unknown-zone", fakeSubnetsToZones)
   654  	c.Assert(err, gc.ErrorMatches, `subnets in AZ "unknown-zone" not found`)
   655  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   656  	c.Check(subnetIDs, gc.IsNil)
   657  }
   658  
   659  func (s *vpcSuite) TestFindSubnetIDsForAvailabilityOneMatched(c *gc.C) {
   660  	subnetIDs, err := findSubnetIDsForAvailabilityZone("az3", fakeSubnetsToZones)
   661  	c.Assert(err, jc.ErrorIsNil)
   662  	c.Check(subnetIDs, gc.DeepEquals, []string{"subnet-oof"})
   663  }
   664  
   665  func (s *vpcSuite) TestFindSubnetIDsForAvailabilityMultipleMatched(c *gc.C) {
   666  	subnetIDs, err := findSubnetIDsForAvailabilityZone("az1", fakeSubnetsToZones)
   667  	c.Assert(err, jc.ErrorIsNil)
   668  	// Result slice of IDs is always sorted.
   669  	c.Check(subnetIDs, gc.DeepEquals, []string{"subnet-bar", "subnet-foo"})
   670  }
   671  
   672  const (
   673  	notDefaultVPC = false
   674  	defaultVPC    = true
   675  
   676  	notMainRouteTable = false
   677  	mainRouteTable    = true
   678  
   679  	noResults = 0
   680  
   681  	anyState     = "any state"
   682  	anyVPCID     = "vpc-anything"
   683  	anyGatewayID = "igw-anything"
   684  	anyTableID   = "rtb-anything"
   685  	anyZone      = "any-zone"
   686  
   687  	noPublicIPOnLaunch   = false
   688  	withPublicIPOnLaunch = true
   689  )
   690  
   691  type stubVPCAPIClient struct {
   692  	*testing.Stub
   693  	vpcAPIClient // embedded mostly for documentation
   694  
   695  	attributesResponse  *ec2.AccountAttributesResp
   696  	vpcsResponse        *ec2.VPCsResp
   697  	subnetsResponse     *ec2.SubnetsResp
   698  	gatewaysResponse    *ec2.InternetGatewaysResp
   699  	routeTablesResponse *ec2.RouteTablesResp
   700  }
   701  
   702  // AccountAttributes implements vpcAPIClient and is used to test finding the
   703  // default VPC from the "default-vpc"" attribute.
   704  func (s *stubVPCAPIClient) AccountAttributes(attributeNames ...string) (*ec2.AccountAttributesResp, error) {
   705  	s.Stub.AddCall("AccountAttributes", makeArgsFromStrings(attributeNames...)...)
   706  	return s.attributesResponse, s.Stub.NextErr()
   707  }
   708  
   709  // VPCs implements vpcAPIClient and is used to test getting the details of a
   710  // VPC.
   711  func (s *stubVPCAPIClient) VPCs(ids []string, filter *ec2.Filter) (*ec2.VPCsResp, error) {
   712  	s.Stub.AddCall("VPCs", ids, filter)
   713  	return s.vpcsResponse, s.Stub.NextErr()
   714  }
   715  
   716  // Subnets implements vpcAPIClient and is used to test getting a VPC's subnets.
   717  func (s *stubVPCAPIClient) Subnets(ids []string, filter *ec2.Filter) (*ec2.SubnetsResp, error) {
   718  	s.Stub.AddCall("Subnets", ids, filter)
   719  	return s.subnetsResponse, s.Stub.NextErr()
   720  }
   721  
   722  // InternetGateways implements vpcAPIClient and is used to test getting the
   723  // attached IGW of a VPC.
   724  func (s *stubVPCAPIClient) InternetGateways(ids []string, filter *ec2.Filter) (*ec2.InternetGatewaysResp, error) {
   725  	s.Stub.AddCall("InternetGateways", ids, filter)
   726  	return s.gatewaysResponse, s.Stub.NextErr()
   727  }
   728  
   729  // RouteTables implements vpcAPIClient and is used to test getting all route
   730  // tables of a VPC, alond with their routes.
   731  func (s *stubVPCAPIClient) RouteTables(ids []string, filter *ec2.Filter) (*ec2.RouteTablesResp, error) {
   732  	s.Stub.AddCall("RouteTables", ids, filter)
   733  	return s.routeTablesResponse, s.Stub.NextErr()
   734  }
   735  
   736  func (s *stubVPCAPIClient) SetAttributesResponse(attributeNameToValues map[string][]string) {
   737  	s.attributesResponse = &ec2.AccountAttributesResp{
   738  		RequestId:  "fake-request-id",
   739  		Attributes: make([]ec2.AccountAttribute, 0, len(attributeNameToValues)),
   740  	}
   741  
   742  	for name, values := range attributeNameToValues {
   743  		attribute := ec2.AccountAttribute{
   744  			Name:   name,
   745  			Values: values,
   746  		}
   747  		s.attributesResponse.Attributes = append(s.attributesResponse.Attributes, attribute)
   748  	}
   749  }
   750  func (s *stubVPCAPIClient) CheckSingleAccountAttributesCall(c *gc.C, attributeNames ...string) {
   751  	s.Stub.CheckCallNames(c, "AccountAttributes")
   752  	s.Stub.CheckCall(c, 0, "AccountAttributes", makeArgsFromStrings(attributeNames...)...)
   753  	s.Stub.ResetCalls()
   754  }
   755  
   756  func (s *stubVPCAPIClient) SetVPCsResponse(numResults int, state string, isDefault bool) {
   757  	s.vpcsResponse = &ec2.VPCsResp{
   758  		RequestId: "fake-request-id",
   759  		VPCs:      make([]ec2.VPC, numResults),
   760  	}
   761  
   762  	for i := range s.vpcsResponse.VPCs {
   763  		id := fmt.Sprintf("vpc-%d", i)
   764  		vpc := makeEC2VPC(id, state)
   765  		vpc.IsDefault = isDefault
   766  		s.vpcsResponse.VPCs[i] = *vpc
   767  	}
   768  }
   769  
   770  func (s *stubVPCAPIClient) CheckSingleVPCsCall(c *gc.C, vpcID string) {
   771  	var nilFilter *ec2.Filter
   772  	s.Stub.CheckCallNames(c, "VPCs")
   773  	s.Stub.CheckCall(c, 0, "VPCs", []string{vpcID}, nilFilter)
   774  	s.Stub.ResetCalls()
   775  }
   776  
   777  func (s *stubVPCAPIClient) SetSubnetsResponse(numResults int, zone string, mapPublicIpOnLaunch bool) {
   778  	s.subnetsResponse = &ec2.SubnetsResp{
   779  		RequestId: "fake-request-id",
   780  		Subnets:   make([]ec2.Subnet, numResults),
   781  	}
   782  
   783  	for i := range s.subnetsResponse.Subnets {
   784  		s.subnetsResponse.Subnets[i] = ec2.Subnet{
   785  			Id:                  fmt.Sprintf("subnet-%d", i),
   786  			VPCId:               anyVPCID,
   787  			State:               anyState,
   788  			AvailZone:           zone,
   789  			CIDRBlock:           fmt.Sprintf("0.1.%d.0/20", i),
   790  			MapPublicIPOnLaunch: mapPublicIpOnLaunch,
   791  		}
   792  	}
   793  }
   794  
   795  func (s *stubVPCAPIClient) CheckSingleSubnetsCall(c *gc.C, vpc *ec2.VPC) {
   796  	var nilIDs []string
   797  	filter := ec2.NewFilter()
   798  	filter.Add("vpc-id", vpc.Id)
   799  
   800  	s.Stub.CheckCallNames(c, "Subnets")
   801  	s.Stub.CheckCall(c, 0, "Subnets", nilIDs, filter)
   802  	s.Stub.ResetCalls()
   803  }
   804  
   805  func (s *stubVPCAPIClient) SetGatewaysResponse(numResults int, attachmentState string) {
   806  	s.gatewaysResponse = &ec2.InternetGatewaysResp{
   807  		RequestId:        "fake-request-id",
   808  		InternetGateways: make([]ec2.InternetGateway, numResults),
   809  	}
   810  
   811  	for i := range s.gatewaysResponse.InternetGateways {
   812  		id := fmt.Sprintf("igw-%d", i)
   813  		gateway := makeEC2InternetGateway(id, attachmentState)
   814  		s.gatewaysResponse.InternetGateways[i] = *gateway
   815  	}
   816  }
   817  
   818  func (s *stubVPCAPIClient) CheckSingleInternetGatewaysCall(c *gc.C, vpc *ec2.VPC) {
   819  	var nilIDs []string
   820  	filter := ec2.NewFilter()
   821  	filter.Add("attachment.vpc-id", vpc.Id)
   822  
   823  	s.Stub.CheckCallNames(c, "InternetGateways")
   824  	s.Stub.CheckCall(c, 0, "InternetGateways", nilIDs, filter)
   825  	s.Stub.ResetCalls()
   826  }
   827  
   828  func (s *stubVPCAPIClient) SetRouteTablesResponse(tables ...*ec2.RouteTable) {
   829  	s.routeTablesResponse = &ec2.RouteTablesResp{
   830  		RequestId: "fake-request-id",
   831  		Tables:    make([]ec2.RouteTable, len(tables)),
   832  	}
   833  
   834  	for i := range s.routeTablesResponse.Tables {
   835  		s.routeTablesResponse.Tables[i] = *tables[i]
   836  	}
   837  }
   838  
   839  func (s *stubVPCAPIClient) CheckSingleRouteTablesCall(c *gc.C, vpc *ec2.VPC) {
   840  	var nilIDs []string
   841  	filter := ec2.NewFilter()
   842  	filter.Add("vpc-id", vpc.Id)
   843  
   844  	s.Stub.CheckCallNames(c, "RouteTables")
   845  	s.Stub.CheckCall(c, 0, "RouteTables", nilIDs, filter)
   846  	s.Stub.ResetCalls()
   847  }
   848  
   849  func (s *stubVPCAPIClient) PrepareValidateVPCResponses() {
   850  	s.SetVPCsResponse(1, availableState, notDefaultVPC)
   851  	s.vpcsResponse.VPCs[0].CIDRBlock = "0.1.0.0/16"
   852  	s.SetSubnetsResponse(1, anyZone, withPublicIPOnLaunch)
   853  	s.SetGatewaysResponse(1, availableState)
   854  	onlyDefaultAndLocalRoutes := makeEC2Routes(
   855  		s.gatewaysResponse.InternetGateways[0].Id,
   856  		s.vpcsResponse.VPCs[0].CIDRBlock,
   857  		activeState,
   858  		0, // no extra routes
   859  	)
   860  	s.SetRouteTablesResponse(
   861  		makeEC2RouteTable(anyTableID, mainRouteTable, nil, onlyDefaultAndLocalRoutes),
   862  	)
   863  }
   864  
   865  func (s *stubVPCAPIClient) CallValidateVPCAndCheckCallsUpToExpectingVPCNotRecommendedError(c *gc.C, lastExpectedCallName string) {
   866  	err := validateVPC(s, anyVPCID)
   867  	c.Assert(err, jc.Satisfies, isVPCNotRecommendedError)
   868  
   869  	allCalls := []string{"VPCs", "Subnets", "InternetGateways", "RouteTables"}
   870  	var expectedCalls []string
   871  	for i := range allCalls {
   872  		expectedCalls = append(expectedCalls, allCalls[i])
   873  		if allCalls[i] == lastExpectedCallName {
   874  			break
   875  		}
   876  	}
   877  	s.CheckCallNames(c, expectedCalls...)
   878  }
   879  
   880  func makeEC2VPC(vpcID, state string) *ec2.VPC {
   881  	return &ec2.VPC{
   882  		Id:    vpcID,
   883  		State: state,
   884  	}
   885  }
   886  
   887  func makeEC2InternetGateway(gatewayID, attachmentState string) *ec2.InternetGateway {
   888  	return &ec2.InternetGateway{
   889  		Id:              gatewayID,
   890  		VPCId:           anyVPCID,
   891  		AttachmentState: attachmentState,
   892  	}
   893  }
   894  
   895  func makeEC2RouteTable(tableID string, isMain bool, associatedSubnetIDs []string, routes []ec2.Route) *ec2.RouteTable {
   896  	table := &ec2.RouteTable{
   897  		Id:     tableID,
   898  		VPCId:  anyVPCID,
   899  		Routes: routes,
   900  	}
   901  
   902  	if isMain {
   903  		table.Associations = []ec2.RouteTableAssociation{{
   904  			Id:      "rtbassoc-main",
   905  			TableId: tableID,
   906  			IsMain:  true,
   907  		}}
   908  	} else {
   909  		table.Associations = make([]ec2.RouteTableAssociation, len(associatedSubnetIDs))
   910  		for i := range associatedSubnetIDs {
   911  			table.Associations[i] = ec2.RouteTableAssociation{
   912  				Id:       fmt.Sprintf("rtbassoc-%d", i),
   913  				TableId:  tableID,
   914  				SubnetId: associatedSubnetIDs[i],
   915  			}
   916  		}
   917  	}
   918  	return table
   919  }
   920  
   921  func makeEC2Routes(defaultRouteGatewayID, localRouteCIDRBlock, state string, numExtraRoutes int) []ec2.Route {
   922  	var routes []ec2.Route
   923  
   924  	if defaultRouteGatewayID != "" {
   925  		routes = append(routes, ec2.Route{
   926  			DestinationCIDRBlock: defaultRouteCIDRBlock,
   927  			GatewayId:            defaultRouteGatewayID,
   928  			State:                state,
   929  		})
   930  	}
   931  
   932  	if localRouteCIDRBlock != "" {
   933  		routes = append(routes, ec2.Route{
   934  			DestinationCIDRBlock: localRouteCIDRBlock,
   935  			GatewayId:            localRouteGatewayID,
   936  			State:                state,
   937  		})
   938  	}
   939  
   940  	if numExtraRoutes > 0 {
   941  		for i := 0; i < numExtraRoutes; i++ {
   942  			routes = append(routes, ec2.Route{
   943  				DestinationCIDRBlock: fmt.Sprintf("0.1.%d.0/24", i),
   944  				State:                state,
   945  			})
   946  		}
   947  	}
   948  
   949  	return routes
   950  }
   951  
   952  func prepareCheckVPCRouteTableRoutesArgs() (*ec2.VPC, *ec2.RouteTable, *ec2.InternetGateway) {
   953  	anyVPC := makeEC2VPC(anyVPCID, anyState)
   954  	anyVPC.CIDRBlock = "0.1.0.0/16"
   955  	anyTable := makeEC2RouteTable(anyTableID, notMainRouteTable, nil, nil)
   956  	anyGateway := makeEC2InternetGateway(anyGatewayID, anyState)
   957  
   958  	return anyVPC, anyTable, anyGateway
   959  }
   960  
   961  func makeEC2Error(statusCode int, code, message, requestID string) error {
   962  	return &ec2.Error{
   963  		StatusCode: statusCode,
   964  		Code:       code,
   965  		Message:    message,
   966  		RequestId:  requestID,
   967  	}
   968  }
   969  
   970  func makeVPCNotFoundError(vpcID string) error {
   971  	return makeEC2Error(
   972  		400,
   973  		"InvalidVpcID.NotFound",
   974  		fmt.Sprintf("The vpc ID '%s' does not exist", vpcID),
   975  		"fake-request-id",
   976  	)
   977  }
   978  
   979  func makeArgsFromStrings(strings ...string) []interface{} {
   980  	args := make([]interface{}, len(strings))
   981  	for i := range strings {
   982  		args[i] = strings[i]
   983  	}
   984  	return args
   985  }