mirror of https://github.com/docker/docs.git
Merge pull request #2828 from jeanlaurent/aws-vpc-id
ec2 Don't force user to use VPC_ID but use the default one when available
This commit is contained in:
commit
fa4f5edf9b
|
@ -10,26 +10,16 @@ parent="smn_machine_drivers"
|
|||
|
||||
# Amazon Web Services
|
||||
|
||||
Create machines on [Amazon Web Services](http://aws.amazon.com). To create machines on [Amazon Web Services](http://aws.amazon.com), you must supply three required parameters:
|
||||
Create machines on [Amazon Web Services](http://aws.amazon.com). To create machines on [Amazon Web Services](http://aws.amazon.com), you must supply two required parameters:
|
||||
|
||||
- Access Key ID
|
||||
- Secret Access Key
|
||||
- VPC ID
|
||||
|
||||
Obtain your IDs and Keys from AWS. To find the VPC ID:
|
||||
|
||||
1. Login to the AWS console
|
||||
2. Go to **Services -> VPC -> Your VPCs**.
|
||||
3. Locate the VPC ID you want from the _VPC_ column.
|
||||
4. Go to **Services -> VPC -> Subnets**. Examine the _Availability Zone_ column to verify that zone `a` exists and matches your VPC ID.
|
||||
|
||||
For example, `us-east1-a` is in the `a` availability zone. If the `a` zone is not present, you can create a new subnet in that zone or specify a different zone when you create the machine.
|
||||
Obtain your IDs and Keys from AWS.
|
||||
|
||||
To create the machine instance, specify `--driver amazonec2` and the three required parameters.
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C********* --amazonec2-vpc-id vpc-****** aws01
|
||||
|
||||
This example assumes the VPC ID was found in the `a` availability zone. Use the`--amazonec2-zone` flag to specify a zone other than the `a` zone. For example, `--amazonec2-zone c` signifies `us-east1-c`.
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C******* aws01
|
||||
|
||||
## Options
|
||||
|
||||
|
@ -38,7 +28,7 @@ This example assumes the VPC ID was found in the `a` availability zone. Use the`
|
|||
- `--amazonec2-session-token`: Your session token for the Amazon Web Services API.
|
||||
- `--amazonec2-ami`: The AMI ID of the instance to use.
|
||||
- `--amazonec2-region`: The region to use when launching the instance.
|
||||
- `--amazonec2-vpc-id`: **required** Your VPC ID to launch the instance in.
|
||||
- `--amazonec2-vpc-id`: Your VPC ID to launch the instance in.
|
||||
- `--amazonec2-zone`: The AWS zone to launch the instance in (i.e. one of a,b,c,d,e).
|
||||
- `--amazonec2-subnet-id`: AWS VPC subnet id.
|
||||
- `--amazonec2-security-group`: AWS VPC security group name.
|
||||
|
@ -81,7 +71,7 @@ Environment variables and default values:
|
|||
| `--amazonec2-session-token` | `AWS_SESSION_TOKEN` | - |
|
||||
| `--amazonec2-ami` | `AWS_AMI` | `ami-5f709f34` |
|
||||
| `--amazonec2-region` | `AWS_DEFAULT_REGION` | `us-east-1` |
|
||||
| **`--amazonec2-vpc-id`** | `AWS_VPC_ID` | - |
|
||||
| `--amazonec2-vpc-id` | `AWS_VPC_ID` | - |
|
||||
| `--amazonec2-zone` | `AWS_ZONE` | `a` |
|
||||
| `--amazonec2-subnet-id` | `AWS_SUBNET_ID` | - |
|
||||
| `--amazonec2-security-group` | `AWS_SECURITY_GROUP` | `docker-machine` |
|
||||
|
@ -109,3 +99,23 @@ Note that a security group will be created and associated to the host. This secu
|
|||
|
||||
If you specify a security group yourself using the `--amazonec2-security-group` flag, the above ports will be checked and opened and the security group modified.
|
||||
If you want more ports to be opened, like application specific ports, use the aws console and modify the configuration manually.
|
||||
|
||||
|
||||
## VPC ID
|
||||
We determine your default vpc id at the start of a command.
|
||||
In some cases, either because your account does not have a default vpc, or you don't want to use the default one, you can specify a vpc with the `--amazonec2-vpc-id` flag.
|
||||
|
||||
To find the VPC ID:
|
||||
|
||||
1. Login to the AWS console
|
||||
2. Go to **Services -> VPC -> Your VPCs**.
|
||||
3. Locate the VPC ID you want from the _VPC_ column.
|
||||
4. Go to **Services -> VPC -> Subnets**. Examine the _Availability Zone_ column to verify that zone `a` exists and matches your VPC ID.
|
||||
|
||||
For example, `us-east1-a` is in the `a` availability zone. If the `a` zone is not present, you can create a new subnet in that zone or specify a different zone when you create the machine.
|
||||
|
||||
To create a machine with a non-default vpc-id:
|
||||
|
||||
$ docker-machine create --driver amazonec2 --amazonec2-access-key AKI******* --amazonec2-secret-key 8T93C********* --amazonec2-vpc-id vpc-****** aws02
|
||||
|
||||
This example assumes the VPC ID was found in the `a` availability zone. Use the`--amazonec2-zone` flag to specify a zone other than the `a` zone. For example, `--amazonec2-zone c` signifies `us-east1-c`.
|
||||
|
|
|
@ -3,6 +3,7 @@ package amazonec2
|
|||
import (
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -46,12 +47,16 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
dockerPort = 2376
|
||||
swarmPort = 3376
|
||||
dockerPort = 2376
|
||||
swarmPort = 3376
|
||||
errorMissingAccessKeyOption = errors.New("amazonec2 driver requires the --amazonec2-access-key option")
|
||||
errorMissingSecretKeyOption = errors.New("amazonec2 driver requires the --amazonec2-secret-key option")
|
||||
errorNoVPCIdFound = errors.New("amazonec2 driver requires either the --amazonec2-subnet-id or --amazonec2-vpc-id option or an AWS Account with a default vpc-id")
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
*drivers.BaseDriver
|
||||
clientFactory func() Ec2Client
|
||||
Id string
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
|
@ -83,6 +88,10 @@ type Driver struct {
|
|||
Monitoring bool
|
||||
}
|
||||
|
||||
type clientFactory interface {
|
||||
build(d *Driver) Ec2Client
|
||||
}
|
||||
|
||||
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
|
||||
return []mcnflag.Flag{
|
||||
mcnflag.StringFlag{
|
||||
|
@ -201,9 +210,9 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
|
|||
}
|
||||
}
|
||||
|
||||
func NewDriver(hostName, storePath string) drivers.Driver {
|
||||
func NewDriver(hostName, storePath string) *Driver {
|
||||
id := generateId()
|
||||
return &Driver{
|
||||
driver := &Driver{
|
||||
Id: id,
|
||||
AMI: defaultAmiId,
|
||||
Region: defaultRegion,
|
||||
|
@ -218,6 +227,24 @@ func NewDriver(hostName, storePath string) drivers.Driver {
|
|||
StorePath: storePath,
|
||||
},
|
||||
}
|
||||
|
||||
driver.clientFactory = driver.buildClient
|
||||
|
||||
return driver
|
||||
}
|
||||
|
||||
func (d *Driver) buildClient() Ec2Client {
|
||||
config := aws.NewConfig()
|
||||
alogger := AwsLogger()
|
||||
config = config.WithRegion(d.Region)
|
||||
config = config.WithCredentials(credentials.NewStaticCredentials(d.AccessKey, d.SecretKey, d.SessionToken))
|
||||
config = config.WithLogger(alogger)
|
||||
config = config.WithLogLevel(aws.LogDebugWithHTTPBody)
|
||||
return ec2.New(session.New(config))
|
||||
}
|
||||
|
||||
func (d *Driver) getClient() Ec2Client {
|
||||
return d.clientFactory()
|
||||
}
|
||||
|
||||
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||
|
@ -258,15 +285,22 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
|||
d.SetSwarmConfigFromFlags(flags)
|
||||
|
||||
if d.AccessKey == "" {
|
||||
return fmt.Errorf("amazonec2 driver requires the --amazonec2-access-key option")
|
||||
return errorMissingAccessKeyOption
|
||||
}
|
||||
|
||||
if d.SecretKey == "" {
|
||||
return fmt.Errorf("amazonec2 driver requires the --amazonec2-secret-key option")
|
||||
return errorMissingSecretKeyOption
|
||||
}
|
||||
|
||||
if d.VpcId == "" {
|
||||
d.VpcId, err = d.getDefaultVPCId()
|
||||
if err != nil {
|
||||
log.Warnf("Couldn't determine your account Default VPC ID : %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
if d.SubnetId == "" && d.VpcId == "" {
|
||||
return fmt.Errorf("amazonec2 driver requires either the --amazonec2-subnet-id or --amazonec2-vpc-id option")
|
||||
return errorNoVPCIdFound
|
||||
}
|
||||
|
||||
if d.SubnetId != "" && d.VpcId != "" {
|
||||
|
@ -608,7 +642,7 @@ func (d *Driver) GetSSHHostname() (string, error) {
|
|||
|
||||
func (d *Driver) GetSSHUsername() string {
|
||||
if d.SSHUser == "" {
|
||||
d.SSHUser = "ubuntu"
|
||||
d.SSHUser = defaultSSHUser
|
||||
}
|
||||
|
||||
return d.SSHUser
|
||||
|
@ -661,16 +695,6 @@ func (d *Driver) Remove() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) getClient() *ec2.EC2 {
|
||||
config := aws.NewConfig()
|
||||
alogger := AwsLogger()
|
||||
config = config.WithRegion(d.Region)
|
||||
config = config.WithCredentials(credentials.NewStaticCredentials(d.AccessKey, d.SecretKey, d.SessionToken))
|
||||
config = config.WithLogger(alogger)
|
||||
config = config.WithLogLevel(aws.LogDebugWithHTTPBody)
|
||||
return ec2.New(session.New(config))
|
||||
}
|
||||
|
||||
func (d *Driver) getInstance() (*ec2.Instance, error) {
|
||||
instances, err := d.getClient().DescribeInstances(&ec2.DescribeInstancesInput{
|
||||
InstanceIds: []*string{&d.InstanceId},
|
||||
|
@ -938,6 +962,21 @@ func (d *Driver) deleteKeyPair() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) getDefaultVPCId() (string, error) {
|
||||
output, err := d.getClient().DescribeAccountAttributes(&ec2.DescribeAccountAttributesInput{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, attribute := range output.AccountAttributes {
|
||||
if *attribute.AttributeName == "default-vpc" {
|
||||
return *attribute.AttributeValues[0].AttributeValue, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("No default-vpc attribute")
|
||||
}
|
||||
|
||||
func generateId() string {
|
||||
rb := make([]byte, 10)
|
||||
_, err := rand.Read(rb)
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/docker/machine/commands/commandstest"
|
||||
|
@ -85,8 +87,7 @@ func getTestDriver() (*Driver, error) {
|
|||
|
||||
d := NewDriver(machineTestName, storePath)
|
||||
d.SetConfigFromFlags(getDefaultTestDriverFlags())
|
||||
drv := d.(*Driver)
|
||||
return drv, nil
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func TestConfigureSecurityGroupPermissionsEmpty(t *testing.T) {
|
||||
|
@ -238,3 +239,69 @@ func TestSetConfigFromFlags(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Empty(t, checkFlags.InvalidFlags)
|
||||
}
|
||||
|
||||
type fakeEC2WithDescribe struct {
|
||||
*ec2.EC2
|
||||
output *ec2.DescribeAccountAttributesOutput
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeEC2WithDescribe) DescribeAccountAttributes(input *ec2.DescribeAccountAttributesInput) (*ec2.DescribeAccountAttributesOutput, error) {
|
||||
return f.output, f.err
|
||||
}
|
||||
|
||||
func TestFindDefaultVPC(t *testing.T) {
|
||||
defaultVpc := "default-vpc"
|
||||
vpcName := "vpc-9999"
|
||||
|
||||
driver := NewDriver("machineFoo", "path")
|
||||
driver.clientFactory = func() Ec2Client {
|
||||
return &fakeEC2WithDescribe{
|
||||
output: &ec2.DescribeAccountAttributesOutput{
|
||||
AccountAttributes: []*ec2.AccountAttribute{
|
||||
{
|
||||
AttributeName: &defaultVpc,
|
||||
AttributeValues: []*ec2.AccountAttributeValue{
|
||||
{AttributeValue: &vpcName},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
vpc, err := driver.getDefaultVPCId()
|
||||
|
||||
assert.Equal(t, "vpc-9999", vpc)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDefaultVPCIsMissing(t *testing.T) {
|
||||
driver := NewDriver("machineFoo", "path")
|
||||
driver.clientFactory = func() Ec2Client {
|
||||
return &fakeEC2WithDescribe{
|
||||
output: &ec2.DescribeAccountAttributesOutput{
|
||||
AccountAttributes: []*ec2.AccountAttribute{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
vpc, err := driver.getDefaultVPCId()
|
||||
|
||||
assert.EqualError(t, err, "No default-vpc attribute")
|
||||
assert.Empty(t, vpc)
|
||||
}
|
||||
|
||||
func TestDescribeAccountAttributeFails(t *testing.T) {
|
||||
driver := NewDriver("machineFoo", "path")
|
||||
driver.clientFactory = func() Ec2Client {
|
||||
return &fakeEC2WithDescribe{
|
||||
err: errors.New("Not Found"),
|
||||
}
|
||||
}
|
||||
|
||||
vpc, err := driver.getDefaultVPCId()
|
||||
|
||||
assert.EqualError(t, err, "Not Found")
|
||||
assert.Empty(t, vpc)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package amazonec2
|
||||
|
||||
import "github.com/aws/aws-sdk-go/service/ec2"
|
||||
|
||||
type Ec2Client interface {
|
||||
DescribeAccountAttributes(input *ec2.DescribeAccountAttributesInput) (*ec2.DescribeAccountAttributesOutput, error)
|
||||
|
||||
DescribeSubnets(input *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error)
|
||||
|
||||
CreateTags(input *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error)
|
||||
|
||||
//SecurityGroup
|
||||
|
||||
CreateSecurityGroup(input *ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error)
|
||||
|
||||
AuthorizeSecurityGroupIngress(input *ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error)
|
||||
|
||||
DescribeSecurityGroups(input *ec2.DescribeSecurityGroupsInput) (*ec2.DescribeSecurityGroupsOutput, error)
|
||||
|
||||
DeleteSecurityGroup(input *ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error)
|
||||
|
||||
//KeyPair
|
||||
|
||||
DeleteKeyPair(input *ec2.DeleteKeyPairInput) (*ec2.DeleteKeyPairOutput, error)
|
||||
|
||||
ImportKeyPair(input *ec2.ImportKeyPairInput) (*ec2.ImportKeyPairOutput, error)
|
||||
|
||||
DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error)
|
||||
|
||||
//Instances
|
||||
|
||||
DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
|
||||
|
||||
StartInstances(input *ec2.StartInstancesInput) (*ec2.StartInstancesOutput, error)
|
||||
|
||||
RebootInstances(input *ec2.RebootInstancesInput) (*ec2.RebootInstancesOutput, error)
|
||||
|
||||
StopInstances(input *ec2.StopInstancesInput) (*ec2.StopInstancesOutput, error)
|
||||
|
||||
RunInstances(input *ec2.RunInstancesInput) (*ec2.Reservation, error)
|
||||
|
||||
TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error)
|
||||
|
||||
//SpotInstances
|
||||
|
||||
RequestSpotInstances(input *ec2.RequestSpotInstancesInput) (*ec2.RequestSpotInstancesOutput, error)
|
||||
|
||||
DescribeSpotInstanceRequests(input *ec2.DescribeSpotInstanceRequestsInput) (*ec2.DescribeSpotInstanceRequestsOutput, error)
|
||||
|
||||
WaitUntilSpotInstanceRequestFulfilled(input *ec2.DescribeSpotInstanceRequestsInput) error
|
||||
}
|
Loading…
Reference in New Issue