Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1092 lines (985 sloc) 32.6 KB
//
// goamz - Go packages to interact with the Amazon Web Services.
//
// https://wiki.ubuntu.com/goamz
//
// Copyright (c) 2011 Canonical Ltd.
//
package ec2
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/xml"
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"sort"
"strconv"
"time"
"gopkg.in/amz.v1/aws"
)
const (
debug = false
// legacyAPIVersion is the AWS API version used for all but
// VPC-related requests.
legacyAPIVersion = "2011-12-15"
// AWS API version used for VPC-related calls.
vpcAPIVersion = "2013-10-15"
)
// The EC2 type encapsulates operations with a specific EC2 region.
type EC2 struct {
aws.Auth
aws.Region
private byte // Reserve the right of using private data.
}
// New creates a new EC2.
func New(auth aws.Auth, region aws.Region) *EC2 {
return &EC2{auth, region, 0}
}
// ----------------------------------------------------------------------------
// Filtering helper.
// Filter builds filtering parameters to be used in an EC2 query which supports
// filtering. For example:
//
// filter := NewFilter()
// filter.Add("architecture", "i386")
// filter.Add("launch-index", "0")
// resp, err := ec2.Instances(nil, filter)
//
type Filter struct {
m map[string][]string
}
// NewFilter creates a new Filter.
func NewFilter() *Filter {
return &Filter{make(map[string][]string)}
}
// Add appends a filtering parameter with the given name and value(s).
func (f *Filter) Add(name string, value ...string) {
f.m[name] = append(f.m[name], value...)
}
func (f *Filter) addParams(params map[string]string) {
if f != nil {
a := make([]string, len(f.m))
i := 0
for k := range f.m {
a[i] = k
i++
}
sort.StringSlice(a).Sort()
for i, k := range a {
prefix := "Filter." + strconv.Itoa(i+1)
params[prefix+".Name"] = k
for j, v := range f.m[k] {
params[prefix+".Value."+strconv.Itoa(j+1)] = v
}
}
}
}
// ----------------------------------------------------------------------------
// Request dispatching logic.
// Error encapsulates an error returned by EC2.
//
// See http://goo.gl/VZGuC for more details.
type Error struct {
// HTTP status code (200, 403, ...)
StatusCode int
// EC2 error code ("UnsupportedOperation", ...)
Code string
// The human-oriented error message
Message string
RequestId string `xml:"RequestID"`
}
func (err *Error) Error() string {
if err.Code == "" {
return err.Message
}
return fmt.Sprintf("%s (%s)", err.Message, err.Code)
}
// For now a single error inst is being exposed. In the future it may be useful
// to provide access to all of them, but rather than doing it as an array/slice,
// use a *next pointer, so that it's backward compatible and it continues to be
// easy to handle the first error, which is what most people will want.
type xmlErrors struct {
RequestId string `xml:"RequestID"`
Errors []Error `xml:"Errors>Error"`
}
var timeNow = time.Now
// resp = response structure that will get inflated by XML unmarshaling.
func (ec2 *EC2) query(params map[string]string, resp interface{}) error {
req, err := http.NewRequest("GET", ec2.Region.EC2Endpoint, nil)
if err != nil {
return err
}
// Add the params passed in to the query string
query := req.URL.Query()
for varName, varVal := range params {
query.Add(varName, varVal)
}
query.Add("Timestamp", timeNow().In(time.UTC).Format(time.RFC3339))
req.URL.RawQuery = query.Encode()
ec2.Region.Sign(req, ec2.Auth)
r, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer r.Body.Close()
if debug {
dump, _ := httputil.DumpResponse(r, true)
log.Printf("response:\n")
log.Printf("%v\n}\n", string(dump))
}
if r.StatusCode != 200 {
return buildError(r)
}
return xml.NewDecoder(r.Body).Decode(resp)
}
func multimap(p map[string]string) url.Values {
q := make(url.Values, len(p))
for k, v := range p {
q[k] = []string{v}
}
return q
}
func buildError(r *http.Response) error {
errors := xmlErrors{}
xml.NewDecoder(r.Body).Decode(&errors)
var err Error
if len(errors.Errors) > 0 {
err = errors.Errors[0]
}
err.RequestId = errors.RequestId
err.StatusCode = r.StatusCode
if err.Message == "" {
err.Message = r.Status
}
return &err
}
func makeParams(action string) map[string]string {
return makeParamsWithVersion(action, legacyAPIVersion)
}
func makeParamsVPC(action string) map[string]string {
return makeParamsWithVersion(action, vpcAPIVersion)
}
func makeParamsWithVersion(action, version string) map[string]string {
params := make(map[string]string)
params["Action"] = action
params["Version"] = version
return params
}
func addParamsList(params map[string]string, label string, ids []string) {
for i, id := range ids {
params[label+"."+strconv.Itoa(i+1)] = id
}
}
// ----------------------------------------------------------------------------
// Instance management functions and types.
// RunNetworkInterface encapsulates options for a single network
// interface, specified when calling RunInstances.
//
// If Id is set, it must match an existing VPC network interface, and
// in this case only a single instance can be launched. If Id is not
// set, a new network interface will be created for each instance.
//
// The following fields are required when creating a new network
// interface (i.e. Id is empty): DeviceIndex, SubnetId, Description
// (only used if set), SecurityGroupIds.
//
// PrivateIPs can be used to add one or more private IP addresses to a
// network interface. Only one of the IP addresses can be set as
// primary. If none are given, EC2 selects a primary IP for each
// created interface from the subnet pool.
//
// When SecondaryPrivateIPCount is non-zero, EC2 allocates that number
// of IP addresses from within the subnet range and sets them as
// secondary IPs. The number of IP addresses that can be assigned to a
// network interface varies by instance type.
type RunNetworkInterface struct {
Id string
DeviceIndex int
SubnetId string
Description string
PrivateIPs []PrivateIP
SecurityGroupIds []string
DeleteOnTermination bool
SecondaryPrivateIPCount int
}
// The RunInstances type encapsulates options for the respective request in EC2.
//
// See http://goo.gl/Mcm3b for more details.
type RunInstances struct {
ImageId string
MinCount int
MaxCount int
KeyName string
InstanceType string
SecurityGroups []SecurityGroup
KernelId string
RamdiskId string
UserData []byte
AvailZone string
PlacementGroupName string
Monitoring bool
SubnetId string
DisableAPITermination bool
ShutdownBehavior string
PrivateIPAddress string
BlockDeviceMappings []BlockDeviceMapping
NetworkInterfaces []RunNetworkInterface
}
// Response to a RunInstances request.
//
// See http://goo.gl/Mcm3b for more details.
type RunInstancesResp struct {
RequestId string `xml:"requestId"`
ReservationId string `xml:"reservationId"`
OwnerId string `xml:"ownerId"`
SecurityGroups []SecurityGroup `xml:"groupSet>item"`
Instances []Instance `xml:"instancesSet>item"`
}
// Instance encapsulates a running instance in EC2.
//
// See http://goo.gl/OCH8a for more details.
type Instance struct {
InstanceId string `xml:"instanceId"`
InstanceType string `xml:"instanceType"`
ImageId string `xml:"imageId"`
PrivateDNSName string `xml:"privateDnsName"`
DNSName string `xml:"dnsName"`
IPAddress string `xml:"ipAddress"`
PrivateIPAddress string `xml:"privateIpAddress"`
SubnetId string `xml:"subnetId"`
VPCId string `xml:"vpcId"`
SourceDestCheck bool `xml:"sourceDestCheck"`
KeyName string `xml:"keyName"`
AMILaunchIndex int `xml:"amiLaunchIndex"`
Hypervisor string `xml:"hypervisor"`
VirtType string `xml:"virtualizationType"`
Monitoring string `xml:"monitoring>state"`
AvailZone string `xml:"placement>availabilityZone"`
PlacementGroupName string `xml:"placement>groupName"`
State InstanceState `xml:"instanceState"`
Tags []Tag `xml:"tagSet>item"`
SecurityGroups []SecurityGroup `xml:"groupSet>item"`
NetworkInterfaces []NetworkInterface `xml:"networkInterfaceSet>item"`
}
// RunInstances starts new instances in EC2.
// If options.MinCount and options.MaxCount are both zero, a single instance
// will be started; otherwise if options.MaxCount is zero, options.MinCount
// will be used instead.
//
// See http://goo.gl/Mcm3b for more details.
func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) {
params := prepareRunParams(*options)
params["ImageId"] = options.ImageId
params["InstanceType"] = options.InstanceType
var min, max int
if options.MinCount == 0 && options.MaxCount == 0 {
min = 1
max = 1
} else if options.MaxCount == 0 {
min = options.MinCount
max = min
} else {
min = options.MinCount
max = options.MaxCount
}
params["MinCount"] = strconv.Itoa(min)
params["MaxCount"] = strconv.Itoa(max)
i, j := 1, 1
for _, g := range options.SecurityGroups {
if g.Id != "" {
params["SecurityGroupId."+strconv.Itoa(i)] = g.Id
i++
} else {
params["SecurityGroup."+strconv.Itoa(j)] = g.Name
j++
}
}
prepareBlockDevices(params, options.BlockDeviceMappings)
prepareNetworkInterfaces(params, options.NetworkInterfaces)
token, err := clientToken()
if err != nil {
return nil, err
}
params["ClientToken"] = token
if options.KeyName != "" {
params["KeyName"] = options.KeyName
}
if options.KernelId != "" {
params["KernelId"] = options.KernelId
}
if options.RamdiskId != "" {
params["RamdiskId"] = options.RamdiskId
}
if options.UserData != nil {
userData := make([]byte, base64.StdEncoding.EncodedLen(len(options.UserData)))
base64.StdEncoding.Encode(userData, options.UserData)
params["UserData"] = string(userData)
}
if options.AvailZone != "" {
params["Placement.AvailabilityZone"] = options.AvailZone
}
if options.PlacementGroupName != "" {
params["Placement.GroupName"] = options.PlacementGroupName
}
if options.Monitoring {
params["Monitoring.Enabled"] = "true"
}
if options.SubnetId != "" {
params["SubnetId"] = options.SubnetId
}
if options.DisableAPITermination {
params["DisableApiTermination"] = "true"
}
if options.ShutdownBehavior != "" {
params["InstanceInitiatedShutdownBehavior"] = options.ShutdownBehavior
}
if options.PrivateIPAddress != "" {
params["PrivateIpAddress"] = options.PrivateIPAddress
}
resp = &RunInstancesResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
func prepareRunParams(options RunInstances) map[string]string {
if options.SubnetId != "" || len(options.NetworkInterfaces) > 0 {
// When either SubnetId or NetworkInterfaces are specified, we
// need to use the API version with complete VPC support.
return makeParamsVPC("RunInstances")
} else {
return makeParams("RunInstances")
}
}
func prepareBlockDevices(params map[string]string, blockDevs []BlockDeviceMapping) {
for i, b := range blockDevs {
n := strconv.Itoa(i + 1)
prefix := "BlockDeviceMapping." + n
if b.DeviceName != "" {
params[prefix+".DeviceName"] = b.DeviceName
}
if b.VirtualName != "" {
params[prefix+".VirtualName"] = b.VirtualName
}
if b.SnapshotId != "" {
params[prefix+".Ebs.SnapshotId"] = b.SnapshotId
}
if b.VolumeType != "" {
params[prefix+".Ebs.VolumeType"] = b.VolumeType
}
if b.VolumeSize > 0 {
params[prefix+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10)
}
if b.IOPS > 0 {
params[prefix+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10)
}
if b.DeleteOnTermination {
params[prefix+".Ebs.DeleteOnTermination"] = "true"
}
}
}
func prepareNetworkInterfaces(params map[string]string, nics []RunNetworkInterface) {
for i, ni := range nics {
// Unlike other lists, NetworkInterface and PrivateIpAddresses
// should start from 0, not 1, according to the examples
// requests in the API documentation here http://goo.gl/Mcm3b.
n := strconv.Itoa(i)
prefix := "NetworkInterface." + n
if ni.Id != "" {
params[prefix+".NetworkInterfaceId"] = ni.Id
}
params[prefix+".DeviceIndex"] = strconv.Itoa(ni.DeviceIndex)
if ni.SubnetId != "" {
params[prefix+".SubnetId"] = ni.SubnetId
}
if ni.Description != "" {
params[prefix+".Description"] = ni.Description
}
for j, gid := range ni.SecurityGroupIds {
k := strconv.Itoa(j + 1)
params[prefix+".SecurityGroupId."+k] = gid
}
if ni.DeleteOnTermination {
params[prefix+".DeleteOnTermination"] = "true"
}
if ni.SecondaryPrivateIPCount > 0 {
val := strconv.Itoa(ni.SecondaryPrivateIPCount)
params[prefix+".SecondaryPrivateIpAddressCount"] = val
}
for j, ip := range ni.PrivateIPs {
k := strconv.Itoa(j)
subprefix := prefix + ".PrivateIpAddresses." + k
params[subprefix+".PrivateIpAddress"] = ip.Address
params[subprefix+".Primary"] = strconv.FormatBool(ip.IsPrimary)
}
}
}
func clientToken() (string, error) {
// Maximum EC2 client token size is 64 bytes.
// Each byte expands to two when hex encoded.
buf := make([]byte, 32)
_, err := rand.Read(buf)
if err != nil {
return "", err
}
return hex.EncodeToString(buf), nil
}
// Response to a TerminateInstances request.
//
// See http://goo.gl/3BKHj for more details.
type TerminateInstancesResp struct {
RequestId string `xml:"requestId"`
StateChanges []InstanceStateChange `xml:"instancesSet>item"`
}
// InstanceState encapsulates the state of an instance in EC2.
//
// See http://goo.gl/y3ZBq for more details.
type InstanceState struct {
Code int `xml:"code"` // Watch out, bits 15-8 have unpublished meaning.
Name string `xml:"name"`
}
// InstanceStateChange informs of the previous and current states
// for an instance when a state change is requested.
type InstanceStateChange struct {
InstanceId string `xml:"instanceId"`
CurrentState InstanceState `xml:"currentState"`
PreviousState InstanceState `xml:"previousState"`
}
// TerminateInstances requests the termination of instances when the given ids.
//
// See http://goo.gl/3BKHj for more details.
func (ec2 *EC2) TerminateInstances(instIds []string) (resp *TerminateInstancesResp, err error) {
params := makeParams("TerminateInstances")
addParamsList(params, "InstanceId", instIds)
resp = &TerminateInstancesResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// Response to a DescribeInstances request.
//
// See http://goo.gl/mLbmw for more details.
type InstancesResp struct {
RequestId string `xml:"requestId"`
Reservations []Reservation `xml:"reservationSet>item"`
}
// Reservation represents details about a reservation in EC2.
//
// See http://goo.gl/0ItPT for more details.
type Reservation struct {
ReservationId string `xml:"reservationId"`
OwnerId string `xml:"ownerId"`
RequesterId string `xml:"requesterId"`
SecurityGroups []SecurityGroup `xml:"groupSet>item"`
Instances []Instance `xml:"instancesSet>item"`
}
// Instances returns details about instances in EC2. Both parameters
// are optional, and if provided will limit the instances returned to those
// matching the given instance ids or filtering rules.
//
// See http://goo.gl/4No7c for more details.
func (ec2 *EC2) Instances(instIds []string, filter *Filter) (resp *InstancesResp, err error) {
params := makeParams("DescribeInstances")
addParamsList(params, "InstanceId", instIds)
filter.addParams(params)
resp = &InstancesResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// ----------------------------------------------------------------------------
// Image and snapshot management functions and types.
// Response to a DescribeImages request.
//
// See http://goo.gl/hLnyg for more details.
type ImagesResp struct {
RequestId string `xml:"requestId"`
Images []Image `xml:"imagesSet>item"`
}
// BlockDeviceMapping represents the association of a block device with an image.
//
// See http://goo.gl/wnDBf for more details.
type BlockDeviceMapping struct {
DeviceName string `xml:"deviceName"`
VirtualName string `xml:"virtualName"`
SnapshotId string `xml:"ebs>snapshotId"`
VolumeType string `xml:"ebs>volumeType"`
VolumeSize int64 `xml:"ebs>volumeSize"` // Size is given in GB
DeleteOnTermination bool `xml:"ebs>deleteOnTermination"`
// The number of I/O operations per second (IOPS) that the volume supports.
IOPS int64 `xml:"ebs>iops"`
}
// Image represents details about an image.
//
// See http://goo.gl/iSqJG for more details.
type Image struct {
Id string `xml:"imageId"`
Name string `xml:"name"`
Description string `xml:"description"`
Type string `xml:"imageType"`
State string `xml:"imageState"`
Location string `xml:"imageLocation"`
Public bool `xml:"isPublic"`
Architecture string `xml:"architecture"`
Platform string `xml:"platform"`
ProductCodes []string `xml:"productCode>item>productCode"`
KernelId string `xml:"kernelId"`
RamdiskId string `xml:"ramdiskId"`
StateReason string `xml:"stateReason"`
OwnerId string `xml:"imageOwnerId"`
OwnerAlias string `xml:"imageOwnerAlias"`
RootDeviceType string `xml:"rootDeviceType"`
RootDeviceName string `xml:"rootDeviceName"`
VirtualizationType string `xml:"virtualizationType"`
Hypervisor string `xml:"hypervisor"`
BlockDevices []BlockDeviceMapping `xml:"blockDeviceMapping>item"`
}
// Images returns details about available images.
// The ids and filter parameters, if provided, will limit the images returned.
// For example, to get all the private images associated with this account set
// the boolean filter "is-private" to true.
//
// Note: calling this function with nil ids and filter parameters will result in
// a very large number of images being returned.
//
// See http://goo.gl/SRBhW for more details.
func (ec2 *EC2) Images(ids []string, filter *Filter) (resp *ImagesResp, err error) {
params := makeParams("DescribeImages")
for i, id := range ids {
params["ImageId."+strconv.Itoa(i+1)] = id
}
filter.addParams(params)
resp = &ImagesResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// Response to a CreateSnapshot request.
//
// See http://goo.gl/ttcda for more details.
type CreateSnapshotResp struct {
RequestId string `xml:"requestId"`
Snapshot
}
// CreateSnapshot creates a volume snapshot and stores it in S3.
//
// See http://goo.gl/ttcda for more details.
func (ec2 *EC2) CreateSnapshot(volumeId, description string) (resp *CreateSnapshotResp, err error) {
params := makeParams("CreateSnapshot")
params["VolumeId"] = volumeId
params["Description"] = description
resp = &CreateSnapshotResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// DeleteSnapshots deletes the volume snapshots with the given ids.
//
// Note: If you make periodic snapshots of a volume, the snapshots are
// incremental so that only the blocks on the device that have changed
// since your last snapshot are incrementally saved in the new snapshot.
// Even though snapshots are saved incrementally, the snapshot deletion
// process is designed so that you need to retain only the most recent
// snapshot in order to restore the volume.
//
// See http://goo.gl/vwU1y for more details.
func (ec2 *EC2) DeleteSnapshots(ids []string) (resp *SimpleResp, err error) {
params := makeParams("DeleteSnapshot")
for i, id := range ids {
params["SnapshotId."+strconv.Itoa(i+1)] = id
}
resp = &SimpleResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// Response to a DescribeSnapshots request.
//
// See http://goo.gl/nClDT for more details.
type SnapshotsResp struct {
RequestId string `xml:"requestId"`
Snapshots []Snapshot `xml:"snapshotSet>item"`
}
// Snapshot represents details about a volume snapshot.
//
// See http://goo.gl/nkovs for more details.
type Snapshot struct {
Id string `xml:"snapshotId"`
VolumeId string `xml:"volumeId"`
VolumeSize string `xml:"volumeSize"`
Status string `xml:"status"`
StartTime string `xml:"startTime"`
Description string `xml:"description"`
Progress string `xml:"progress"`
OwnerId string `xml:"ownerId"`
OwnerAlias string `xml:"ownerAlias"`
Tags []Tag `xml:"tagSet>item"`
}
// Snapshots returns details about volume snapshots available to the user.
// The ids and filter parameters, if provided, limit the snapshots returned.
//
// See http://goo.gl/ogJL4 for more details.
func (ec2 *EC2) Snapshots(ids []string, filter *Filter) (resp *SnapshotsResp, err error) {
params := makeParams("DescribeSnapshots")
for i, id := range ids {
params["SnapshotId."+strconv.Itoa(i+1)] = id
}
filter.addParams(params)
resp = &SnapshotsResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// ----------------------------------------------------------------------------
// Security group management functions and types.
// SimpleResp represents a response to an EC2 request which on success will
// return no other information besides a request id.
type SimpleResp struct {
XMLName xml.Name
RequestId string `xml:"requestId"`
}
// CreateSecurityGroupResp represents a response to a CreateSecurityGroup request.
type CreateSecurityGroupResp struct {
SecurityGroup
RequestId string `xml:"requestId"`
}
// CreateSecurityGroup creates a security group with the provided name
// and description.
//
// See http://goo.gl/Eo7Yl for more details.
func (ec2 *EC2) CreateSecurityGroup(name, description string) (resp *CreateSecurityGroupResp, err error) {
return ec2.CreateSecurityGroupVPC("", name, description)
}
// CreateSecurityGroupVPC creates a security group in EC2, associated
// with the given VPC ID. If vpcId is empty, this call is equivalent
// to CreateSecurityGroup.
//
// See http://goo.gl/Eo7Yl for more details.
func (ec2 *EC2) CreateSecurityGroupVPC(vpcId, name, description string) (resp *CreateSecurityGroupResp, err error) {
params := makeParamsVPC("CreateSecurityGroup")
params["GroupName"] = name
params["GroupDescription"] = description
if vpcId != "" {
params["VpcId"] = vpcId
}
resp = &CreateSecurityGroupResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
resp.Name = name
return resp, nil
}
// SecurityGroupsResp represents a response to a DescribeSecurityGroups
// request in EC2.
//
// See http://goo.gl/k12Uy for more details.
type SecurityGroupsResp struct {
RequestId string `xml:"requestId"`
Groups []SecurityGroupInfo `xml:"securityGroupInfo>item"`
}
// SecurityGroup encapsulates details for a security group in EC2.
//
// See http://goo.gl/CIdyP for more details.
type SecurityGroupInfo struct {
SecurityGroup
VPCId string `xml:"vpcId"`
OwnerId string `xml:"ownerId"`
Description string `xml:"groupDescription"`
IPPerms []IPPerm `xml:"ipPermissions>item"`
}
// IPPerm represents an allowance within an EC2 security group.
//
// See http://goo.gl/4oTxv for more details.
type IPPerm struct {
Protocol string `xml:"ipProtocol"`
FromPort int `xml:"fromPort"`
ToPort int `xml:"toPort"`
SourceIPs []string `xml:"ipRanges>item>cidrIp"`
SourceGroups []UserSecurityGroup `xml:"groups>item"`
}
// UserSecurityGroup holds a security group and the owner
// of that group.
type UserSecurityGroup struct {
Id string `xml:"groupId"`
Name string `xml:"groupName"`
OwnerId string `xml:"userId"`
}
// SecurityGroup represents an EC2 security group.
// If SecurityGroup is used as a parameter, then one of Id or Name
// may be empty. If both are set, then Id is used.
type SecurityGroup struct {
Id string `xml:"groupId"`
Name string `xml:"groupName"`
}
// SecurityGroupNames is a convenience function that
// returns a slice of security groups with the given names.
func SecurityGroupNames(names ...string) []SecurityGroup {
g := make([]SecurityGroup, len(names))
for i, name := range names {
g[i] = SecurityGroup{Name: name}
}
return g
}
// SecurityGroupNames is a convenience function that
// returns a slice of security groups with the given ids.
func SecurityGroupIds(ids ...string) []SecurityGroup {
g := make([]SecurityGroup, len(ids))
for i, id := range ids {
g[i] = SecurityGroup{Id: id}
}
return g
}
// SecurityGroups returns details about security groups in EC2. Both parameters
// are optional, and if provided will limit the security groups returned to those
// matching the given groups or filtering rules.
//
// See http://goo.gl/k12Uy for more details.
func (ec2 *EC2) SecurityGroups(groups []SecurityGroup, filter *Filter) (resp *SecurityGroupsResp, err error) {
params := makeParams("DescribeSecurityGroups")
i, j := 1, 1
for _, g := range groups {
if g.Id != "" {
params["GroupId."+strconv.Itoa(i)] = g.Id
i++
} else {
params["GroupName."+strconv.Itoa(j)] = g.Name
j++
}
}
filter.addParams(params)
resp = &SecurityGroupsResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// DeleteSecurityGroup removes the given security group in EC2.
//
// See http://goo.gl/QJJDO for more details.
func (ec2 *EC2) DeleteSecurityGroup(group SecurityGroup) (resp *SimpleResp, err error) {
params := makeParams("DeleteSecurityGroup")
if group.Id != "" {
params["GroupId"] = group.Id
} else {
params["GroupName"] = group.Name
}
resp = &SimpleResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// AuthorizeSecurityGroup creates an allowance for clients matching the provided
// rules to access instances within the given security group.
//
// See http://goo.gl/u2sDJ for more details.
func (ec2 *EC2) AuthorizeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
return ec2.authOrRevoke("AuthorizeSecurityGroupIngress", group, perms)
}
// RevokeSecurityGroup revokes permissions from a group.
//
// See http://goo.gl/ZgdxA for more details.
func (ec2 *EC2) RevokeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
return ec2.authOrRevoke("RevokeSecurityGroupIngress", group, perms)
}
func (ec2 *EC2) authOrRevoke(op string, group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
params := makeParams(op)
if group.Id != "" {
params["GroupId"] = group.Id
} else {
params["GroupName"] = group.Name
}
for i, perm := range perms {
prefix := "IpPermissions." + strconv.Itoa(i+1)
params[prefix+".IpProtocol"] = perm.Protocol
params[prefix+".FromPort"] = strconv.Itoa(perm.FromPort)
params[prefix+".ToPort"] = strconv.Itoa(perm.ToPort)
for j, ip := range perm.SourceIPs {
params[prefix+".IpRanges."+strconv.Itoa(j+1)+".CidrIp"] = ip
}
for j, g := range perm.SourceGroups {
subprefix := prefix + ".Groups." + strconv.Itoa(j+1)
if g.OwnerId != "" {
params[subprefix+".UserId"] = g.OwnerId
}
if g.Id != "" {
params[subprefix+".GroupId"] = g.Id
} else {
params[subprefix+".GroupName"] = g.Name
}
}
}
resp = &SimpleResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// Tag represents key-value metadata used to classify and organize
// EC2 instances.
//
// See http://goo.gl/bncl3 for more details
type Tag struct {
Key string `xml:"key"`
Value string `xml:"value"`
}
// CreateTags adds or overwrites one or more tags for the specified instance ids.
//
// See http://goo.gl/Vmkqc for more details
func (ec2 *EC2) CreateTags(instIds []string, tags []Tag) (resp *SimpleResp, err error) {
params := makeParams("CreateTags")
addParamsList(params, "ResourceId", instIds)
for j, tag := range tags {
params["Tag."+strconv.Itoa(j+1)+".Key"] = tag.Key
params["Tag."+strconv.Itoa(j+1)+".Value"] = tag.Value
}
resp = &SimpleResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// Response to a StartInstances request.
//
// See http://goo.gl/awKeF for more details.
type StartInstanceResp struct {
RequestId string `xml:"requestId"`
StateChanges []InstanceStateChange `xml:"instancesSet>item"`
}
// Response to a StopInstances request.
//
// See http://goo.gl/436dJ for more details.
type StopInstanceResp struct {
RequestId string `xml:"requestId"`
StateChanges []InstanceStateChange `xml:"instancesSet>item"`
}
// StartInstances starts an Amazon EBS-backed AMI that you've previously stopped.
//
// See http://goo.gl/awKeF for more details.
func (ec2 *EC2) StartInstances(ids ...string) (resp *StartInstanceResp, err error) {
params := makeParams("StartInstances")
addParamsList(params, "InstanceId", ids)
resp = &StartInstanceResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// StopInstances requests stopping one or more Amazon EBS-backed instances.
//
// See http://goo.gl/436dJ for more details.
func (ec2 *EC2) StopInstances(ids ...string) (resp *StopInstanceResp, err error) {
params := makeParams("StopInstances")
addParamsList(params, "InstanceId", ids)
resp = &StopInstanceResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// RebootInstance requests a reboot of one or more instances. This operation is asynchronous;
// it only queues a request to reboot the specified instance(s). The operation will succeed
// if the instances are valid and belong to you.
//
// Requests to reboot terminated instances are ignored.
//
// See http://goo.gl/baoUf for more details.
func (ec2 *EC2) RebootInstances(ids ...string) (resp *SimpleResp, err error) {
params := makeParams("RebootInstances")
addParamsList(params, "InstanceId", ids)
resp = &SimpleResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// ----------------------------------------------------------------------------
// Availability zone management functions and types.
// See http://goo.gl/ylxT4R for more details.
// AvailabilityZonesResp represents a response to a DescribeAvailabilityZones
// request in EC2.
type AvailabilityZonesResp struct {
RequestId string `xml:"requestId"`
Zones []AvailabilityZoneInfo `xml:"availabilityZoneInfo>item"`
}
// AvailabilityZoneInfo encapsulates details for an availability zone in EC2.
type AvailabilityZoneInfo struct {
AvailabilityZone
State string `xml:"zoneState"`
MessageSet []string `xml:"messageSet>item"`
}
// AvailabilityZone represents an EC2 availability zone.
type AvailabilityZone struct {
Name string `xml:"zoneName"`
Region string `xml:"regionName"`
}
// AvailabilityZones returns details about availability zones in EC2.
// The filter parameter is optional, and if provided will limit the
// availability zones returned to those matching the given filtering
// rules.
//
// See http://goo.gl/ylxT4R for more details.
func (ec2 *EC2) AvailabilityZones(filter *Filter) (resp *AvailabilityZonesResp, err error) {
params := makeParams("DescribeAvailabilityZones")
filter.addParams(params)
resp = &AvailabilityZonesResp{}
err = ec2.query(params, resp)
if err != nil {
return nil, err
}
return
}
// AccountAttribute holds information about an account attribute.
//
// See http://goo.gl/hBc28j for more details.
type AccountAttribute struct {
Name string `xml:"attributeName"`
Values []string `xml:"attributeValueSet>item>attributeValue"`
}
// AccountAttributesResp is the response to an AccountAttributes request.
//
// See http://goo.gl/hBc28j for more details.
type AccountAttributesResp struct {
RequestId string `xml:"requestId"`
Attributes []AccountAttribute `xml:"accountAttributeSet>item"`
}
// AccountAttributes returns the values of one or more account
// attributes.
//
// See http://goo.gl/hBc28j for more details.
func (ec2 *EC2) AccountAttributes(attrNames ...string) (*AccountAttributesResp, error) {
params := makeParamsVPC("DescribeAccountAttributes")
for i, attrName := range attrNames {
params["AttributeName."+strconv.Itoa(i+1)] = attrName
}
resp := &AccountAttributesResp{}
if err := ec2.query(params, resp); err != nil {
return nil, err
}
return resp, nil
}