linuxkit: Add run support for AWS

Signed-off-by: Dave Tucker <dt@docker.com>
This commit is contained in:
Dave Tucker 2017-06-05 14:54:28 +01:00
parent 0737694eb7
commit 3dcd8a2730
2 changed files with 189 additions and 0 deletions

View File

@ -16,6 +16,7 @@ func runUsage() {
fmt.Printf("'backend' specifies the run backend.\n")
fmt.Printf("If not specified the platform specific default will be used\n")
fmt.Printf("Supported backends are (default platform in brackets):\n")
fmt.Printf(" aws\n")
fmt.Printf(" azure\n")
fmt.Printf(" gcp\n")
fmt.Printf(" hyperkit [macOS]\n")
@ -40,6 +41,8 @@ func run(args []string) {
case "help", "-h", "-help", "--help":
runUsage()
os.Exit(0)
case "aws":
runAWS(args[1:])
case "hyperkit":
runHyperKit(args[1:])
case "azure":

186
src/cmd/linuxkit/run_aws.go Normal file
View File

@ -0,0 +1,186 @@
package main
import (
"encoding/base64"
"flag"
"fmt"
"os"
"path/filepath"
log "github.com/Sirupsen/logrus"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
)
const (
defaultAWSMachine = "t2.micro"
defaultAWSDiskSize = 0
defaultAWSDiskType = "gp2"
defaultAWSZone = "a"
// Environment variables. Some are non-standard
awsMachineVar = "AWS_MACHINE" // non-standard
awsDiskSizeVar = "AWS_DISK_SIZE" // non-standard
awsDiskTypeVar = "AWS_DISK_TYPE" // non-standard
awsZoneVar = "AWS_ZONE" // non-standard
)
// Process the run arguments and execute run
func runAWS(args []string) {
flags := flag.NewFlagSet("aws", flag.ExitOnError)
invoked := filepath.Base(os.Args[0])
flags.Usage = func() {
fmt.Printf("USAGE: %s run aws [options] [name]\n\n", invoked)
fmt.Printf("'name' is the name of an AWS image that has already been\n")
fmt.Printf(" uploaded using 'linuxkit push'\n\n")
fmt.Printf("Options:\n\n")
flags.PrintDefaults()
}
machineFlag := flags.String("machine", defaultAWSMachine, "AWS Machine Type")
diskSizeFlag := flags.Int("disk-size", 0, "Size of system disk in GB")
diskTypeFlag := flags.String("disk-type", defaultAWSDiskType, "AWS Disk Type")
zoneFlag := flags.String("zone", defaultAWSZone, "AWS Availability Zone")
if err := flags.Parse(args); err != nil {
log.Fatal("Unable to parse args")
}
remArgs := flags.Args()
if len(remArgs) == 0 {
fmt.Printf("Please specify the name of the image to boot\n")
flags.Usage()
os.Exit(1)
}
name := remArgs[0]
machine := getStringValue(awsMachineVar, *machineFlag, defaultAWSMachine)
diskSize := getIntValue(awsDiskSizeVar, *diskSizeFlag, defaultAWSDiskSize)
diskType := getStringValue(awsDiskTypeVar, *diskTypeFlag, defaultAWSDiskType)
zone := os.Getenv("AWS_REGION") + getStringValue(awsZoneVar, *zoneFlag, defaultAWSZone)
sess := session.Must(session.NewSession())
compute := ec2.New(sess)
// 1. Find AMI
filter := &ec2.DescribeImagesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("name"),
Values: []*string{aws.String(name)},
},
},
}
results, err := compute.DescribeImages(filter)
if err != nil {
log.Fatalf("Unable to describe images: %s", err)
}
if len(results.Images) < 1 {
log.Fatalf("Unable to find image with name %s", name)
}
if len(results.Images) > 1 {
log.Warnf("Found multiple images with the same name, using the first one")
}
imageID := results.Images[0].ImageId
// 2. Create Instance
params := &ec2.RunInstancesInput{
ImageId: imageID,
InstanceType: aws.String(machine),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
}
runResult, err := compute.RunInstances(params)
if err != nil {
log.Fatalf("Unable to run instance: %s", err)
}
instanceID := runResult.Instances[0].InstanceId
log.Infof("Created instance %s", *instanceID)
instanceFilter := &ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("instance-id"),
Values: []*string{instanceID},
},
},
}
if err = compute.WaitUntilInstanceRunning(instanceFilter); err != nil {
log.Fatalf("Error waiting for instance to start: %s", err)
}
log.Infof("Instance %s is running", *instanceID)
if diskSize > 0 {
// 3. Create EBS Volume
diskParams := &ec2.CreateVolumeInput{
AvailabilityZone: aws.String(zone),
Size: aws.Int64(int64(diskSize)),
VolumeType: aws.String(diskType),
}
log.Debugf("CreateVolume:\n%v\n", diskParams)
volume, err := compute.CreateVolume(diskParams)
if err != nil {
log.Fatalf("Error creating volume: %s", err)
}
waitVol := &ec2.DescribeVolumesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("volume-id"),
Values: []*string{volume.VolumeId},
},
},
}
log.Infof("Waiting for volume %s to be available", *volume.VolumeId)
if err := compute.WaitUntilVolumeAvailable(waitVol); err != nil {
log.Fatalf("Error waiting for volume to be available: %s", err)
}
log.Infof("Attaching volume %s to instance %s", *volume.VolumeId, *instanceID)
volParams := &ec2.AttachVolumeInput{
Device: aws.String("/dev/sda2"),
InstanceId: instanceID,
VolumeId: volume.VolumeId,
}
_, err = compute.AttachVolume(volParams)
if err != nil {
log.Fatalf("Error attaching volume to instance: %s", err)
}
}
log.Warnf("AWS doesn't stream serial console output.\n Please use the AWS Management Console to obtain this output \n Console ouput will be displayed when the instance has been stopped.")
log.Warn("Waiting for instance to stop...")
if err = compute.WaitUntilInstanceStopped(instanceFilter); err != nil {
log.Fatalf("Error waiting for instance to stop: %s", err)
}
consoleParams := &ec2.GetConsoleOutputInput{
InstanceId: instanceID,
}
output, err := compute.GetConsoleOutput(consoleParams)
if err != nil {
log.Fatalf("Error getting output from instance %s: %s", *instanceID, err)
}
out, err := base64.StdEncoding.DecodeString(*output.Output)
if err != nil {
log.Fatalf("Error decoding output: %s", err)
}
fmt.Printf(string(out) + "\n")
log.Infof("Terminating instance %s", *instanceID)
terminateParams := &ec2.TerminateInstancesInput{
InstanceIds: []*string{instanceID},
}
if _, err := compute.TerminateInstances(terminateParams); err != nil {
log.Fatalf("Error terminating instance %s", *instanceID)
}
if err = compute.WaitUntilInstanceTerminated(instanceFilter); err != nil {
log.Fatalf("Error waiting for instance to terminate: %s", err)
}
}