diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 48b7bba05ed..d4dcebbff70 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -140,11 +140,15 @@ }, { "ImportPath": "github.com/mitchellh/goamz/aws", - "Rev": "9cad7da945e699385c1a3e115aa255211921c9bb" + "Rev": "703cfb45985762869e465f37ed030ff01615ff1e" }, { "ImportPath": "github.com/mitchellh/goamz/ec2", - "Rev": "9cad7da945e699385c1a3e115aa255211921c9bb" + "Rev": "703cfb45985762869e465f37ed030ff01615ff1e" + }, + { + "ImportPath": "github.com/mitchellh/goamz/elb", + "Rev": "703cfb45985762869e465f37ed030ff01615ff1e" }, { "ImportPath": "github.com/mitchellh/mapstructure", diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/aws/aws.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/aws/aws.go index c304d5540ea..cfc42c03ab4 100644 --- a/Godeps/_workspace/src/github.com/mitchellh/goamz/aws/aws.go +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/aws/aws.go @@ -13,9 +13,10 @@ import ( "encoding/json" "errors" "fmt" - "github.com/vaughan0/go-ini" "io/ioutil" "os" + + "github.com/vaughan0/go-ini" ) // Region defines the URLs where AWS services may be accessed. @@ -123,6 +124,23 @@ var EUWest = Region{ "https://route53.amazonaws.com", } +var EUCentral = Region{ + "eu-central-1", + "https://ec2.eu-central-1.amazonaws.com", + "https://s3-eu-central-1.amazonaws.com", + "", + true, + true, + "", + "https://sns.eu-central-1.amazonaws.com", + "https://sqs.eu-central-1.amazonaws.com", + "https://iam.amazonaws.com", + "https://elasticloadbalancing.eu-central-1.amazonaws.com", + "https://autoscaling.eu-central-1.amazonaws.com", + "https://rds.eu-central-1.amazonaws.com", + "https://route53.amazonaws.com", +} + var APSoutheast = Region{ "ap-southeast-1", "https://ec2.ap-southeast-1.amazonaws.com", @@ -213,6 +231,7 @@ var Regions = map[string]Region{ APSoutheast.Name: APSoutheast, APSoutheast2.Name: APSoutheast2, EUWest.Name: EUWest, + EUCentral.Name: EUCentral, USEast.Name: USEast, USWest.Name: USWest, USWest2.Name: USWest2, @@ -334,13 +353,16 @@ func SharedAuth() (auth Auth, err error) { profileName = "default" } - var homeDir = os.Getenv("HOME") - if homeDir == "" { - err = errors.New("Could not get HOME") - return + var credentialsFile = os.Getenv("AWS_CREDENTIAL_FILE") + if credentialsFile == "" { + var homeDir = os.Getenv("HOME") + if homeDir == "" { + err = errors.New("Could not get HOME") + return + } + credentialsFile = homeDir + "/.aws/credentials" } - var credentialsFile = homeDir + "/.aws/credentials" file, err := ini.LoadFile(credentialsFile) if err != nil { err = errors.New("Couldn't parse AWS credentials file") diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2.go index 8f94ad539fb..c0c9b6712bf 100644 --- a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2.go +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2.go @@ -127,7 +127,7 @@ type xmlErrors struct { var timeNow = time.Now func (ec2 *EC2) query(params map[string]string, resp interface{}) error { - params["Version"] = "2014-05-01" + params["Version"] = "2014-06-15" params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339) endpoint, err := url.Parse(ec2.Region.EC2Endpoint) if err != nil { @@ -178,7 +178,7 @@ func buildError(r *http.Response) error { err.RequestId = errors.RequestId err.StatusCode = r.StatusCode if err.Message == "" { - err.Message = r.Status + err.Message = err.Code } return &err } @@ -203,29 +203,32 @@ func addBlockDeviceParams(prename string, params map[string]string, blockdevices if k.DeviceName != "" { params[prefix+"DeviceName"] = k.DeviceName } + if k.VirtualName != "" { params[prefix+"VirtualName"] = k.VirtualName - } - if k.SnapshotId != "" { - params[prefix+"Ebs.SnapshotId"] = k.SnapshotId - } - if k.VolumeType != "" { - params[prefix+"Ebs.VolumeType"] = k.VolumeType - } - if k.IOPS != 0 { - params[prefix+"Ebs.Iops"] = strconv.FormatInt(k.IOPS, 10) - } - if k.VolumeSize != 0 { - params[prefix+"Ebs.VolumeSize"] = strconv.FormatInt(k.VolumeSize, 10) - } - if k.DeleteOnTermination { - params[prefix+"Ebs.DeleteOnTermination"] = "true" - } - if k.Encrypted { - params[prefix+"Ebs.Encrypted"] = "true" - } - if k.NoDevice { + } else if k.NoDevice { params[prefix+"NoDevice"] = "" + } else { + if k.SnapshotId != "" { + params[prefix+"Ebs.SnapshotId"] = k.SnapshotId + } + if k.VolumeType != "" { + params[prefix+"Ebs.VolumeType"] = k.VolumeType + } + if k.IOPS != 0 { + params[prefix+"Ebs.Iops"] = strconv.FormatInt(k.IOPS, 10) + } + if k.VolumeSize != 0 { + params[prefix+"Ebs.VolumeSize"] = strconv.FormatInt(k.VolumeSize, 10) + } + if k.DeleteOnTermination { + params[prefix+"Ebs.DeleteOnTermination"] = "true" + } else { + params[prefix+"Ebs.DeleteOnTermination"] = "false" + } + if k.Encrypted { + params[prefix+"Ebs.Encrypted"] = "true" + } } } } @@ -253,9 +256,11 @@ type RunInstances struct { SubnetId string AssociatePublicIpAddress bool DisableAPITermination bool + EbsOptimized bool ShutdownBehavior string PrivateIPAddress string BlockDevices []BlockDeviceMapping + Tenancy string } // Response to a RunInstances request. @@ -269,6 +274,15 @@ type RunInstancesResp struct { Instances []Instance `xml:"instancesSet>item"` } +// BlockDevice represents the association of a block device with an instance. +type BlockDevice struct { + DeviceName string `xml:"deviceName"` + VolumeId string `xml:"ebs>volumeId"` + Status string `xml:"ebs>status"` + AttachTime string `xml:"ebs>attachTime"` + DeleteOnTermination bool `xml:"ebs>deleteOnTermination"` +} + // Instance encapsulates a running instance in EC2. // // See http://goo.gl/OCH8a for more details. @@ -284,6 +298,7 @@ type Instance struct { VirtType string `xml:"virtualizationType"` Monitoring string `xml:"monitoring>state"` AvailZone string `xml:"placement>availabilityZone"` + Tenancy string `xml:"placement>tenancy"` PlacementGroupName string `xml:"placement>groupName"` State InstanceState `xml:"instanceState"` Tags []Tag `xml:"tagSet>item"` @@ -296,6 +311,8 @@ type Instance struct { LaunchTime time.Time `xml:"launchTime"` SourceDestCheck bool `xml:"sourceDestCheck"` SecurityGroups []SecurityGroup `xml:"groupSet>item"` + EbsOptimized string `xml:"ebsOptimized"` + BlockDevices []BlockDevice `xml:"blockDeviceMapping>item"` } // RunInstances starts new instances in EC2. @@ -350,6 +367,9 @@ func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err if options.Monitoring { params["Monitoring.Enabled"] = "true" } + if options.Tenancy != "" { + params["Placement.Tenancy"] = options.Tenancy + } if options.SubnetId != "" && options.AssociatePublicIpAddress { // If we have a non-default VPC / Subnet specified, we can flag // AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided. @@ -362,6 +382,10 @@ func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err params["NetworkInterface.0.AssociatePublicIpAddress"] = "true" params["NetworkInterface.0.SubnetId"] = options.SubnetId + if options.PrivateIPAddress != "" { + params["NetworkInterface.0.PrivateIpAddress"] = options.PrivateIPAddress + } + i := 1 for _, g := range options.SecurityGroups { // We only have SecurityGroupId's on NetworkInterface's, no SecurityGroup params. @@ -375,6 +399,10 @@ func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err params["SubnetId"] = options.SubnetId } + if options.PrivateIPAddress != "" { + params["PrivateIpAddress"] = options.PrivateIPAddress + } + i, j := 1, 1 for _, g := range options.SecurityGroups { if g.Id != "" { @@ -392,12 +420,12 @@ func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err if options.DisableAPITermination { params["DisableApiTermination"] = "true" } + if options.EbsOptimized { + params["EbsOptimized"] = "true" + } if options.ShutdownBehavior != "" { params["InstanceInitiatedShutdownBehavior"] = options.ShutdownBehavior } - if options.PrivateIPAddress != "" { - params["PrivateIpAddress"] = options.PrivateIPAddress - } addBlockDeviceParams("", params, options.BlockDevices) resp = &RunInstancesResp{} @@ -419,6 +447,111 @@ func clientToken() (string, error) { return hex.EncodeToString(buf), nil } +// The GetConsoleOutput type encapsulates options for the respective request in EC2. +// +// See http://goo.gl/EY70zb for more details. +type GetConsoleOutput struct { + InstanceId string +} + +// Response to a GetConsoleOutput request. Note that Output is base64-encoded, +// as in the underlying AWS API. +// +// See http://goo.gl/EY70zb for more details. +type GetConsoleOutputResp struct { + RequestId string `xml:"requestId"` + InstanceId string `xml:"instanceId"` + Timestamp time.Time `xml:"timestamp"` + Output string `xml:"output"` +} + +// GetConsoleOutput returns the console output for the sepcified instance. Note +// that console output is base64-encoded, as in the underlying AWS API. +// +// See http://goo.gl/EY70zb for more details. +func (ec2 *EC2) GetConsoleOutput(options *GetConsoleOutput) (resp *GetConsoleOutputResp, err error) { + params := makeParams("GetConsoleOutput") + params["InstanceId"] = options.InstanceId + resp = &GetConsoleOutputResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ---------------------------------------------------------------------------- +// Instance events and status functions and types. + +// The DescribeInstanceStatus type encapsulates options for the respective request in EC2. +// +// See http://goo.gl/DFySJY for more details. +type EventsSet struct { + Code string `xml:"code"` + Description string `xml:"description"` + NotBefore string `xml:"notBefore"` + NotAfter string `xml:"notAfter"` +} + +type StatusDetails struct { + Name string `xml:"name"` + Status string `xml:"status"` + ImpairedSince string `xml:"impairedSince"` +} + +type Status struct { + Status string `xml:"status"` + Details []StatusDetails `xml:"details>item"` +} + +type InstanceStatusSet struct { + InstanceId string `xml:"instanceId"` + AvailabilityZone string `xml:"availabilityZone"` + InstanceState InstanceState `xml:"instanceState"` + SystemStatus Status `xml:"systemStatus"` + InstanceStatus Status `xml:"instanceStatus"` + Events []EventsSet `xml:"eventsSet>item"` +} + +type DescribeInstanceStatusResp struct { + RequestId string `xml:"requestId"` + InstanceStatus []InstanceStatusSet `xml:"instanceStatusSet>item"` +} + +type DescribeInstanceStatus struct { + InstanceIds []string + IncludeAllInstances bool + MaxResults int64 + NextToken string +} + +func (ec2 *EC2) DescribeInstanceStatus(options *DescribeInstanceStatus, filter *Filter) (resp *DescribeInstanceStatusResp, err error) { + params := makeParams("DescribeInstanceStatus") + if options.IncludeAllInstances { + params["IncludeAllInstances"] = "true" + } + if len(options.InstanceIds) > 0 { + addParamsList(params, "InstanceId", options.InstanceIds) + } + if options.MaxResults > 0 { + params["MaxResults"] = strconv.FormatInt(options.MaxResults, 10) + } + if options.NextToken != "" { + params["NextToken"] = options.NextToken + } + if filter != nil { + filter.addParams(params) + } + + resp = &DescribeInstanceStatusResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + // ---------------------------------------------------------------------------- // Spot Instance management functions and types. @@ -643,6 +776,61 @@ func (ec2 *EC2) CancelSpotRequests(spotrequestIds []string) (resp *CancelSpotReq return } +type DescribeSpotPriceHistory struct { + InstanceType []string + ProductDescription []string + AvailabilityZone string + StartTime, EndTime time.Time +} + +// Response to a DescribeSpotPriceHisotyr request. +// +// See http://goo.gl/3BKHj for more details. +type DescribeSpotPriceHistoryResp struct { + RequestId string `xml:"requestId"` + History []SpotPriceHistory `xml:"spotPriceHistorySet>item"` +} + +type SpotPriceHistory struct { + InstanceType string `xml:"instanceType"` + ProductDescription string `xml:"productDescription"` + SpotPrice string `xml:"spotPrice"` + Timestamp time.Time `xml:"timestamp"` + AvailabilityZone string `xml:"availabilityZone"` +} + +// DescribeSpotPriceHistory gets the spot pricing history. +// +// See http://goo.gl/3BKHj for more details. +func (ec2 *EC2) DescribeSpotPriceHistory(o *DescribeSpotPriceHistory) (resp *DescribeSpotPriceHistoryResp, err error) { + params := makeParams("DescribeSpotPriceHistory") + if o.AvailabilityZone != "" { + params["AvailabilityZone"] = o.AvailabilityZone + } + + if !o.StartTime.IsZero() { + params["StartTime"] = o.StartTime.In(time.UTC).Format(time.RFC3339) + } + if !o.EndTime.IsZero() { + params["EndTime"] = o.EndTime.In(time.UTC).Format(time.RFC3339) + } + + if len(o.InstanceType) > 0 { + addParamsList(params, "InstanceType", o.InstanceType) + } + if len(o.ProductDescription) > 0 { + addParamsList(params, "ProductDescription", o.ProductDescription) + } + + resp = &DescribeSpotPriceHistoryResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + // Response to a TerminateInstances request. // // See http://goo.gl/3BKHj for more details. @@ -871,6 +1059,47 @@ func (ec2 *EC2) Volumes(volIds []string, filter *Filter) (resp *VolumesResp, err return } +// ---------------------------------------------------------------------------- +// Availability zone management functions and types. +// See http://goo.gl/ylxT4R for more details. + +// DescribeAvailabilityZonesResp represents a response to a DescribeAvailabilityZones +// request in EC2. +type DescribeAvailabilityZonesResp 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"` +} + +// DescribeAvailabilityZones 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) DescribeAvailabilityZones(filter *Filter) (resp *DescribeAvailabilityZonesResp, err error) { + params := makeParams("DescribeAvailabilityZones") + filter.addParams(params) + resp = &DescribeAvailabilityZonesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + // ---------------------------------------------------------------------------- // ElasticIp management (for VPC) @@ -1003,6 +1232,20 @@ func (ec2 *EC2) DisassociateAddress(id string) (resp *SimpleResp, err error) { return } +// Disassociate an address from a VPC instance. +func (ec2 *EC2) DisassociateAddressClassic(ip string) (resp *SimpleResp, err error) { + params := makeParams("DisassociateAddress") + params["PublicIp"] = ip + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + // DescribeAddresses returns details about one or more // Elastic IP Addresses. Returned addresses can be // filtered by Public IP, Allocation ID or multiple filters @@ -1683,6 +1926,7 @@ type SecurityGroup struct { Name string `xml:"groupName"` Description string `xml:"groupDescription"` VpcId string `xml:"vpcId"` + Tags []Tag `xml:"tagSet>item"` } // SecurityGroupNames is a convenience function that @@ -1841,6 +2085,28 @@ func (ec2 *EC2) CreateTags(resourceIds []string, tags []Tag) (resp *SimpleResp, return resp, nil } +// DeleteTags deletes tags. +func (ec2 *EC2) DeleteTags(resourceIds []string, tags []Tag) (resp *SimpleResp, err error) { + params := makeParams("DeleteTags") + addParamsList(params, "ResourceId", resourceIds) + + for j, tag := range tags { + params["Tag."+strconv.Itoa(j+1)+".Key"] = tag.Key + + if tag.Value != "" { + 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 +} + type TagsResp struct { RequestId string `xml:"requestId"` Tags []ResourceTag `xml:"tagSet>item"` @@ -2125,6 +2391,38 @@ type CreateSubnetResp struct { Subnet Subnet `xml:"subnet"` } +// The ModifySubnetAttribute request parameters +// +// http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-ModifySubnetAttribute.html +type ModifySubnetAttribute struct { + SubnetId string + MapPublicIpOnLaunch bool +} + +type ModifySubnetAttributeResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// The CreateNetworkAcl request parameters +// +// http://goo.gl/BZmCRF +type CreateNetworkAcl struct { + VpcId string +} + +// Response to a CreateNetworkAcl request +type CreateNetworkAclResp struct { + RequestId string `xml:"requestId"` + NetworkAcl NetworkAcl `xml:"networkAcl"` +} + +// Response to CreateNetworkAclEntry request +type CreateNetworkAclEntryResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + // Response to a DescribeInternetGateways request. type InternetGatewaysResp struct { RequestId string `xml:"requestId"` @@ -2195,6 +2493,52 @@ type Subnet struct { Tags []Tag `xml:"tagSet>item"` } +// NetworkAcl represent network acl +type NetworkAcl struct { + NetworkAclId string `xml:"networkAclId"` + VpcId string `xml:"vpcId"` + Default string `xml:"default"` + EntrySet []NetworkAclEntry `xml:"entrySet>item"` + AssociationSet []NetworkAclAssociation `xml:"associationSet>item"` + Tags []Tag `xml:"tagSet>item"` +} + +// NetworkAclAssociation +type NetworkAclAssociation struct { + NetworkAclAssociationId string `xml:"networkAclAssociationId"` + NetworkAclId string `xml:"networkAclId"` + SubnetId string `xml:"subnetId"` +} + +// NetworkAclEntry represent a rule within NetworkAcl +type NetworkAclEntry struct { + RuleNumber int `xml:"ruleNumber"` + Protocol int `xml:"protocol"` + RuleAction string `xml:"ruleAction"` + Egress bool `xml:"egress"` + CidrBlock string `xml:"cidrBlock"` + IcmpCode IcmpCode `xml:"icmpTypeCode"` + PortRange PortRange `xml:"portRange"` +} + +// IcmpCode +type IcmpCode struct { + Code int `xml:"code"` + Type int `xml:"type"` +} + +// PortRange +type PortRange struct { + From int `xml:"from"` + To int `xml:"to"` +} + +// Response to describe NetworkAcls +type NetworkAclsResp struct { + RequestId string `xml:"requestId"` + NetworkAcls []NetworkAcl `xml:"networkAclSet>item"` +} + // VPC represents a single VPC. type VPC struct { VpcId string `xml:"vpcId"` @@ -2331,6 +2675,26 @@ func (ec2 *EC2) DeleteSubnet(id string) (resp *SimpleResp, err error) { return } +// ModifySubnetAttribute +// +// http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-ModifySubnetAttribute.html +func (ec2 *EC2) ModifySubnetAttribute(options *ModifySubnetAttribute) (resp *ModifySubnetAttributeResp, err error) { + params := makeParams("ModifySubnetAttribute") + params["SubnetId"] = options.SubnetId + if options.MapPublicIpOnLaunch { + params["MapPublicIpOnLaunch.Value"] = "true" + } else { + params["MapPublicIpOnLaunch.Value"] = "false" + } + + resp = &ModifySubnetAttributeResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + // DescribeSubnets // // http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeSubnets.html @@ -2348,6 +2712,130 @@ func (ec2 *EC2) DescribeSubnets(ids []string, filter *Filter) (resp *SubnetsResp return } +// CreateNetworkAcl creates a network ACL in a VPC. +// +// http://goo.gl/51X7db +func (ec2 *EC2) CreateNetworkAcl(options *CreateNetworkAcl) (resp *CreateNetworkAclResp, err error) { + params := makeParams("CreateNetworkAcl") + params["VpcId"] = options.VpcId + + resp = &CreateNetworkAclResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// CreateNetworkAclEntry creates an entry (a rule) in a network ACL with the specified rule number. +// +// http://goo.gl/BtXhtj +func (ec2 *EC2) CreateNetworkAclEntry(networkAclId string, options *NetworkAclEntry) (resp *CreateNetworkAclEntryResp, err error) { + + params := makeParams("CreateNetworkAclEntry") + params["NetworkAclId"] = networkAclId + params["RuleNumber"] = strconv.Itoa(options.RuleNumber) + params["Protocol"] = strconv.Itoa(options.Protocol) + params["RuleAction"] = options.RuleAction + params["Egress"] = strconv.FormatBool(options.Egress) + params["CidrBlock"] = options.CidrBlock + if params["Protocol"] == "-1" { + params["Icmp.Type"] = strconv.Itoa(options.IcmpCode.Type) + params["Icmp.Code"] = strconv.Itoa(options.IcmpCode.Code) + } + params["PortRange.From"] = strconv.Itoa(options.PortRange.From) + params["PortRange.To"] = strconv.Itoa(options.PortRange.To) + + resp = &CreateNetworkAclEntryResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +// NetworkAcls describes one or more of your network ACLs for given filter. +// +// http://goo.gl/mk9RsV +func (ec2 *EC2) NetworkAcls(networkAclIds []string, filter *Filter) (resp *NetworkAclsResp, err error) { + params := makeParams("DescribeNetworkAcls") + addParamsList(params, "NetworkAclId", networkAclIds) + filter.addParams(params) + resp = &NetworkAclsResp{} + if err = ec2.query(params, resp); err != nil { + return nil, err + } + + return resp, nil +} + +// Response to a DeleteNetworkAcl request. +type DeleteNetworkAclResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// DeleteNetworkAcl deletes the network ACL with specified id. +// +// http://goo.gl/nC78Wx +func (ec2 *EC2) DeleteNetworkAcl(id string) (resp *DeleteNetworkAclResp, err error) { + params := makeParams("DeleteNetworkAcl") + params["NetworkAclId"] = id + + resp = &DeleteNetworkAclResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// Response to a DeleteNetworkAclEntry request. +type DeleteNetworkAclEntryResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// DeleteNetworkAclEntry deletes the specified ingress or egress entry (rule) from the specified network ACL. +// +// http://goo.gl/moQbE2 +func (ec2 *EC2) DeleteNetworkAclEntry(id string, ruleNumber int, egress bool) (resp *DeleteNetworkAclEntryResp, err error) { + params := makeParams("DeleteNetworkAclEntry") + params["NetworkAclId"] = id + params["RuleNumber"] = strconv.Itoa(ruleNumber) + params["Egress"] = strconv.FormatBool(egress) + + resp = &DeleteNetworkAclEntryResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +type ReplaceNetworkAclAssociationResponse struct { + RequestId string `xml:"requestId"` + NewAssociationId string `xml:"newAssociationId"` +} + +// ReplaceNetworkAclAssociation changes which network ACL a subnet is associated with. +// +// http://goo.gl/ar0MH5 +func (ec2 *EC2) ReplaceNetworkAclAssociation(associationId string, networkAclId string) (resp *ReplaceNetworkAclAssociationResponse, err error) { + params := makeParams("ReplaceNetworkAclAssociation") + params["NetworkAclId"] = networkAclId + params["AssociationId"] = associationId + + resp = &ReplaceNetworkAclAssociationResponse{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + // Create a new internet gateway. func (ec2 *EC2) CreateInternetGateway( options *CreateInternetGateway) (resp *CreateInternetGatewayResp, err error) { diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2_test.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2_test.go index 849bfe2e6bb..3ea2bdc75ed 100644 --- a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2_test.go +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2_test.go @@ -1,12 +1,11 @@ package ec2_test import ( - "testing" - "github.com/mitchellh/goamz/aws" "github.com/mitchellh/goamz/ec2" "github.com/mitchellh/goamz/testutil" . "github.com/motain/gocheck" + "testing" ) func Test(t *testing.T) { @@ -95,13 +94,13 @@ func (s *S) TestRunInstancesErrorWithoutXML(c *C) { testServer.WaitRequest() c.Assert(resp, IsNil) - c.Assert(err, ErrorMatches, "500 Internal Server Error") + c.Assert(err, ErrorMatches, "") ec2err, ok := err.(*ec2.Error) c.Assert(ok, Equals, true) c.Assert(ec2err.StatusCode, Equals, 500) c.Assert(ec2err.Code, Equals, "") - c.Assert(ec2err.Message, Equals, "500 Internal Server Error") + c.Assert(ec2err.Message, Equals, "") c.Assert(ec2err.RequestId, Equals, "") } @@ -114,13 +113,13 @@ func (s *S) TestRequestSpotInstancesErrorWithoutXML(c *C) { testServer.WaitRequest() c.Assert(resp, IsNil) - c.Assert(err, ErrorMatches, "500 Internal Server Error") + c.Assert(err, ErrorMatches, "") ec2err, ok := err.(*ec2.Error) c.Assert(ok, Equals, true) c.Assert(ec2err.StatusCode, Equals, 500) c.Assert(ec2err.Code, Equals, "") - c.Assert(ec2err.Message, Equals, "500 Internal Server Error") + c.Assert(ec2err.Message, Equals, "") c.Assert(ec2err.RequestId, Equals, "") } @@ -136,10 +135,12 @@ func (s *S) TestRunInstancesExample(c *C) { KernelId: "kernel-id", RamdiskId: "ramdisk-id", AvailZone: "zone", + Tenancy: "dedicated", PlacementGroupName: "group", Monitoring: true, SubnetId: "subnet-id", DisableAPITermination: true, + EbsOptimized: true, ShutdownBehavior: "terminate", PrivateIPAddress: "10.0.0.25", BlockDevices: []ec2.BlockDeviceMapping{ @@ -168,6 +169,7 @@ func (s *S) TestRunInstancesExample(c *C) { c.Assert(req.Form["Monitoring.Enabled"], DeepEquals, []string{"true"}) c.Assert(req.Form["SubnetId"], DeepEquals, []string{"subnet-id"}) c.Assert(req.Form["DisableApiTermination"], DeepEquals, []string{"true"}) + c.Assert(req.Form["EbsOptimized"], DeepEquals, []string{"true"}) c.Assert(req.Form["InstanceInitiatedShutdownBehavior"], DeepEquals, []string{"terminate"}) c.Assert(req.Form["PrivateIpAddress"], DeepEquals, []string{"10.0.0.25"}) c.Assert(req.Form["BlockDeviceMapping.1.DeviceName"], DeepEquals, []string{"/dev/sdb"}) @@ -304,6 +306,7 @@ func (s *S) TestTerminateInstancesExample(c *C) { c.Assert(req.Form["Monitoring.Enabled"], IsNil) c.Assert(req.Form["SubnetId"], IsNil) c.Assert(req.Form["DisableApiTermination"], IsNil) + c.Assert(req.Form["EbsOptimized"], IsNil) c.Assert(req.Form["InstanceInitiatedShutdownBehavior"], IsNil) c.Assert(req.Form["PrivateIpAddress"], IsNil) @@ -372,6 +375,13 @@ func (s *S) TestDescribeInstancesExample1(c *C) { c.Assert(r0i.PrivateDNSName, Equals, "domU-12-31-39-10-56-34.compute-1.internal") c.Assert(r0i.DNSName, Equals, "ec2-174-129-165-232.compute-1.amazonaws.com") c.Assert(r0i.AvailZone, Equals, "us-east-1b") + + b0 := r0i.BlockDevices[0] + c.Assert(b0.DeviceName, Equals, "/dev/sda1") + c.Assert(b0.VolumeId, Equals, "vol-a082c1c9") + c.Assert(b0.Status, Equals, "attached") + c.Assert(b0.AttachTime, Equals, "2010-08-17T01:15:21.000Z") + c.Assert(b0.DeleteOnTermination, Equals, false) } func (s *S) TestDescribeInstancesExample2(c *C) { @@ -1044,7 +1054,32 @@ func (s *S) TestSignatureWithEndpointPath(c *C) { c.Assert(err, IsNil) req := testServer.WaitRequest() - c.Assert(req.Form["Signature"], DeepEquals, []string{"QmvgkYGn19WirCuCz/jRp3RmRgFwWR5WRkKZ5AZnyXQ="}) + c.Assert(req.Form["Signature"], DeepEquals, []string{"tyOTQ0c0T5ujskCPTWa5ATMtv7UyErgT339cU8O2+Q8="}) +} + +func (s *S) TestDescribeInstanceStatusExample(c *C) { + testServer.Response(200, nil, DescribeInstanceStatusExample) + options := &ec2.DescribeInstanceStatus{} + resp, err := s.ec2.DescribeInstanceStatus(options, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstanceStatus"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "3be1508e-c444-4fef-89cc-0b1223c4f02fEXAMPLE") + c.Assert(resp.InstanceStatus[0].InstanceId, Equals, "i-1a2b3c4d") + c.Assert(resp.InstanceStatus[0].InstanceState.Code, Equals, 16) + c.Assert(resp.InstanceStatus[0].SystemStatus.Status, Equals, "impaired") + c.Assert(resp.InstanceStatus[0].SystemStatus.Details[0].Name, Equals, "reachability") + c.Assert(resp.InstanceStatus[0].SystemStatus.Details[0].Status, Equals, "failed") + c.Assert(resp.InstanceStatus[0].SystemStatus.Details[0].ImpairedSince, Equals, "YYYY-MM-DDTHH:MM:SS.000Z") + c.Assert(resp.InstanceStatus[0].InstanceStatus.Details[0].Name, Equals, "reachability") + c.Assert(resp.InstanceStatus[0].InstanceStatus.Details[0].Status, Equals, "failed") + c.Assert(resp.InstanceStatus[0].InstanceStatus.Details[0].ImpairedSince, Equals, "YYYY-MM-DDTHH:MM:SS.000Z") + c.Assert(resp.InstanceStatus[0].Events[0].Code, Equals, "instance-retirement") + c.Assert(resp.InstanceStatus[0].Events[0].Description, Equals, "The instance is running on degraded hardware") + c.Assert(resp.InstanceStatus[0].Events[0].NotBefore, Equals, "YYYY-MM-DDTHH:MM:SS+0000") + c.Assert(resp.InstanceStatus[0].Events[0].NotAfter, Equals, "YYYY-MM-DDTHH:MM:SS+0000") } func (s *S) TestAllocateAddressExample(c *C) { @@ -1229,6 +1264,24 @@ func (s *S) TestCreateSubnet(c *C) { c.Assert(resp.Subnet.AvailableIpAddressCount, Equals, 251) } +func (s *S) TestModifySubnetAttribute(c *C) { + testServer.Response(200, nil, ModifySubnetAttributeExample) + + options := &ec2.ModifySubnetAttribute{ + SubnetId: "foo", + MapPublicIpOnLaunch: true, + } + + resp, err := s.ec2.ModifySubnetAttribute(options) + + req := testServer.WaitRequest() + c.Assert(req.Form["SubnetId"], DeepEquals, []string{"foo"}) + c.Assert(req.Form["MapPublicIpOnLaunch.Value"], DeepEquals, []string{"true"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + func (s *S) TestResetImageAttribute(c *C) { testServer.Response(200, nil, ResetImageAttributeExample) @@ -1241,3 +1294,145 @@ func (s *S) TestResetImageAttribute(c *C) { c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } + +func (s *S) TestDescribeAvailabilityZonesExample1(c *C) { + testServer.Response(200, nil, DescribeAvailabilityZonesExample1) + + resp, err := s.ec2.DescribeAvailabilityZones(nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAvailabilityZones"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Zones, HasLen, 4) + + z0 := resp.Zones[0] + c.Assert(z0.Name, Equals, "us-east-1a") + c.Assert(z0.Region, Equals, "us-east-1") + c.Assert(z0.State, Equals, "available") + c.Assert(z0.MessageSet, HasLen, 0) + + z1 := resp.Zones[1] + c.Assert(z1.Name, Equals, "us-east-1b") + c.Assert(z1.Region, Equals, "us-east-1") + c.Assert(z1.State, Equals, "available") + c.Assert(z1.MessageSet, HasLen, 0) + + z2 := resp.Zones[2] + c.Assert(z2.Name, Equals, "us-east-1c") + c.Assert(z2.Region, Equals, "us-east-1") + c.Assert(z2.State, Equals, "available") + c.Assert(z2.MessageSet, HasLen, 0) + + z3 := resp.Zones[3] + c.Assert(z3.Name, Equals, "us-east-1d") + c.Assert(z3.Region, Equals, "us-east-1") + c.Assert(z3.State, Equals, "available") + c.Assert(z3.MessageSet, HasLen, 0) +} + +func (s *S) TestDescribeAvailabilityZonesExample2(c *C) { + testServer.Response(200, nil, DescribeAvailabilityZonesExample2) + + resp, err := s.ec2.DescribeAvailabilityZones(nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAvailabilityZones"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Zones, HasLen, 2) + + z0 := resp.Zones[0] + c.Assert(z0.Name, Equals, "us-east-1a") + c.Assert(z0.Region, Equals, "us-east-1") + c.Assert(z0.State, Equals, "impaired") + c.Assert(z0.MessageSet, HasLen, 0) + + z1 := resp.Zones[1] + c.Assert(z1.Name, Equals, "us-east-1b") + c.Assert(z1.Region, Equals, "us-east-1") + c.Assert(z1.State, Equals, "unavailable") + c.Assert(z1.MessageSet, DeepEquals, []string{"us-east-1b is currently down for maintenance."}) +} + +func (s *S) TestCreateNetworkAcl(c *C) { + testServer.Response(200, nil, CreateNetworkAclExample) + + options := &ec2.CreateNetworkAcl{ + VpcId: "vpc-11ad4878", + } + + resp, err := s.ec2.CreateNetworkAcl(options) + + req := testServer.WaitRequest() + c.Assert(req.Form["VpcId"], DeepEquals, []string{"vpc-11ad4878"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.NetworkAcl.VpcId, Equals, "vpc-11ad4878") + c.Assert(resp.NetworkAcl.NetworkAclId, Equals, "acl-5fb85d36") + c.Assert(resp.NetworkAcl.Default, Equals, "false") + c.Assert(resp.NetworkAcl.EntrySet, HasLen, 2) + c.Assert(resp.NetworkAcl.EntrySet[0].RuleNumber, Equals, 32767) + c.Assert(resp.NetworkAcl.EntrySet[0].Protocol, Equals, -1) + c.Assert(resp.NetworkAcl.EntrySet[0].RuleAction, Equals, "deny") + c.Assert(resp.NetworkAcl.EntrySet[0].Egress, Equals, true) + c.Assert(resp.NetworkAcl.EntrySet[0].CidrBlock, Equals, "0.0.0.0/0") +} + +func (s *S) TestCreateNetworkAclEntry(c *C) { + testServer.Response(200, nil, CreateNetworkAclEntryRespExample) + + options := &ec2.NetworkAclEntry{ + RuleNumber: 32767, + Protocol: 6, + RuleAction: "deny", + Egress: true, + CidrBlock: "0.0.0.0/0", + PortRange: ec2.PortRange{ + To: 22, + From: 22, + }, + } + + resp, err := s.ec2.CreateNetworkAclEntry("acl-11ad4878", options) + + req := testServer.WaitRequest() + + c.Assert(req.Form["NetworkAclId"], DeepEquals, []string{"acl-11ad4878"}) + c.Assert(req.Form["RuleNumber"], DeepEquals, []string{"32767"}) + c.Assert(req.Form["Protocol"], DeepEquals, []string{"6"}) + c.Assert(req.Form["RuleAction"], DeepEquals, []string{"deny"}) + c.Assert(req.Form["Egress"], DeepEquals, []string{"true"}) + c.Assert(req.Form["CidrBlock"], DeepEquals, []string{"0.0.0.0/0"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestDescribeNetworkAcls(c *C) { + testServer.Response(200, nil, DescribeNetworkAclsExample) + + filter := ec2.NewFilter() + filter.Add("vpc-id", "vpc-5266953b") + + resp, err := s.ec2.NetworkAcls([]string{"acl-5566953c", "acl-5d659634"}, filter) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.NetworkAcls, HasLen, 2) + c.Assert(resp.NetworkAcls[1].AssociationSet, HasLen, 2) + c.Assert(resp.NetworkAcls[1].AssociationSet[0].NetworkAclAssociationId, Equals, "aclassoc-5c659635") + c.Assert(resp.NetworkAcls[1].AssociationSet[0].NetworkAclId, Equals, "acl-5d659634") + c.Assert(resp.NetworkAcls[1].AssociationSet[0].SubnetId, Equals, "subnet-ff669596") +} + +func (s *S) TestReplaceNetworkAclAssociation(c *C) { + testServer.Response(200, nil, ReplaceNetworkAclAssociationResponseExample) + + resp, err := s.ec2.ReplaceNetworkAclAssociation("aclassoc-e5b95c8c", "acl-5fb85d36") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.NewAssociationId, Equals, "aclassoc-17b85d7e") +} diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2i_test.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2i_test.go index 3773041bf52..8b025dfb408 100644 --- a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2i_test.go +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/ec2i_test.go @@ -166,6 +166,7 @@ var allRegions = []aws.Region{ aws.USEast, aws.USWest, aws.EUWest, + aws.EUCentral, aws.APSoutheast, aws.APNortheast, } diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/responses_test.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/responses_test.go index 0a4dbb366bb..94a681c72fd 100644 --- a/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/responses_test.go +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/ec2/responses_test.go @@ -766,6 +766,137 @@ var AllocateAddressExample = ` ` +// http://goo.gl/DFySJY +var DescribeInstanceStatusExample = ` + + 3be1508e-c444-4fef-89cc-0b1223c4f02fEXAMPLE + + + i-1a2b3c4d + us-east-1d + + 16 + running + + + impaired +
+ + reachability + failed + YYYY-MM-DDTHH:MM:SS.000Z + +
+
+ + impaired +
+ + reachability + failed + YYYY-MM-DDTHH:MM:SS.000Z + +
+
+ + + instance-retirement + The instance is running on degraded hardware + YYYY-MM-DDTHH:MM:SS+0000 + YYYY-MM-DDTHH:MM:SS+0000 + + +
+ + i-2a2b3c4d + us-east-1d + + 16 + running + + + ok +
+ + reachability + passed + +
+
+ + ok +
+ + reachability + passed + +
+
+ + + instance-reboot + The instance is scheduled for a reboot + YYYY-MM-DDTHH:MM:SS+0000 + YYYY-MM-DDTHH:MM:SS+0000 + + +
+ + i-3a2b3c4d + us-east-1c + + 16 + running + + + ok +
+ + reachability + passed + +
+
+ + ok +
+ + reachability + passed + +
+
+
+ + i-4a2b3c4d + us-east-1c + + 16 + running + + + ok +
+ + reachability + passed + +
+
+ + insufficient-data +
+ + reachability + insufficient-data + +
+
+
+
+
+` + // http://goo.gl/3Q0oCc var ReleaseAddressExample = ` @@ -845,6 +976,14 @@ var CreateSubnetExample = ` ` +// http://goo.gl/tu2Kxm +var ModifySubnetAttributeExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + // http://goo.gl/r6ZCPm var ResetImageAttributeExample = ` @@ -852,3 +991,217 @@ var ResetImageAttributeExample = ` true ` + +// http://goo.gl/ylxT4R +var DescribeAvailabilityZonesExample1 = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + us-east-1a + available + us-east-1 + + + + us-east-1b + available + us-east-1 + + + + us-east-1c + available + us-east-1 + + + + us-east-1d + available + us-east-1 + + + + +` + +// http://goo.gl/ylxT4R +var DescribeAvailabilityZonesExample2 = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + us-east-1a + impaired + us-east-1 + + + + us-east-1b + unavailable + us-east-1 + + us-east-1b is currently down for maintenance. + + + + +` + +// http://goo.gl/sdomyE +var CreateNetworkAclExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + acl-5fb85d36 + vpc-11ad4878 + false + + + 32767 + -1 + deny + true + 0.0.0.0/0 + + + 32767 + -1 + deny + false + 0.0.0.0/0 + + + + + + +` + +// http://goo.gl/6sYloC +var CreateNetworkAclEntryRespExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/5tqceF +var DescribeNetworkAclsExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + acl-5566953c + vpc-5266953b + true + + + 100 + -1 + allow + true + 0.0.0.0/0 + + + 32767 + -1 + deny + true + 0.0.0.0/0 + + + 100 + -1 + allow + false + 0.0.0.0/0 + + + 32767 + -1 + deny + false + 0.0.0.0/0 + + + + + + + acl-5d659634 + vpc-5266953b + false + + + 110 + 6 + allow + true + 0.0.0.0/0 + + 49152 + 65535 + + + + 32767 + -1 + deny + true + 0.0.0.0/0 + + + 110 + 6 + allow + false + 0.0.0.0/0 + + 80 + 80 + + + + 120 + 6 + allow + false + 0.0.0.0/0 + + 443 + 443 + + + + 32767 + -1 + deny + false + 0.0.0.0/0 + + + + + aclassoc-5c659635 + acl-5d659634 + subnet-ff669596 + + + aclassoc-c26596ab + acl-5d659634 + subnet-f0669599 + + + + + + +` + +var ReplaceNetworkAclAssociationResponseExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + aclassoc-17b85d7e + +` diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb.go new file mode 100644 index 00000000000..e0d53f3879c --- /dev/null +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb.go @@ -0,0 +1,575 @@ +// The elb package provides types and functions for interaction with the AWS +// Elastic Load Balancing service (ELB) +package elb + +import ( + "encoding/xml" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/mitchellh/goamz/aws" +) + +// The ELB type encapsulates operations operations with the elb endpoint. +type ELB struct { + aws.Auth + aws.Region + httpClient *http.Client +} + +const APIVersion = "2012-06-01" + +// New creates a new ELB instance. +func New(auth aws.Auth, region aws.Region) *ELB { + return NewWithClient(auth, region, aws.RetryingClient) +} + +func NewWithClient(auth aws.Auth, region aws.Region, httpClient *http.Client) *ELB { + return &ELB{auth, region, httpClient} +} + +func (elb *ELB) query(params map[string]string, resp interface{}) error { + params["Version"] = APIVersion + params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) + + endpoint, err := url.Parse(elb.Region.ELBEndpoint) + if err != nil { + return err + } + + sign(elb.Auth, "GET", "/", params, endpoint.Host) + endpoint.RawQuery = multimap(params).Encode() + r, err := elb.httpClient.Get(endpoint.String()) + + if err != nil { + return err + } + defer r.Body.Close() + if r.StatusCode > 200 { + return buildError(r) + } + + decoder := xml.NewDecoder(r.Body) + decodedBody := decoder.Decode(resp) + + return decodedBody +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +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 makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +// ---------------------------------------------------------------------------- +// ELB objects + +// A listener attaches to an elb +type Listener struct { + InstancePort int64 `xml:"Listener>InstancePort"` + InstanceProtocol string `xml:"Listener>InstanceProtocol"` + SSLCertificateId string `xml:"Listener>SSLCertificateId"` + LoadBalancerPort int64 `xml:"Listener>LoadBalancerPort"` + Protocol string `xml:"Listener>Protocol"` +} + +// An Instance attaches to an elb +type Instance struct { + InstanceId string `xml:"InstanceId"` +} + +// A tag attached to an elb +type Tag struct { + Key string `xml:"Key"` + Value string `xml:"Value"` +} + +// An InstanceState from an elb health query +type InstanceState struct { + InstanceId string `xml:"InstanceId"` + Description string `xml:"Description"` + State string `xml:"State"` + ReasonCode string `xml:"ReasonCode"` +} + +// ---------------------------------------------------------------------------- +// AddTags + +type AddTags struct { + LoadBalancerNames []string + Tags []Tag +} + +type AddTagsResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) AddTags(options *AddTags) (resp *AddTagsResp, err error) { + params := makeParams("AddTags") + + for i, v := range options.LoadBalancerNames { + params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v + } + + for i, v := range options.Tags { + params["Tags.member."+strconv.Itoa(i+1)+".Key"] = v.Key + params["Tags.member."+strconv.Itoa(i+1)+".Value"] = v.Value + } + + resp = &AddTagsResp{} + + err = elb.query(params, resp) + + return resp, err +} + +// ---------------------------------------------------------------------------- +// RemoveTags + +type RemoveTags struct { + LoadBalancerNames []string + TagKeys []string +} + +type RemoveTagsResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) RemoveTags(options *RemoveTags) (resp *RemoveTagsResp, err error) { + params := makeParams("RemoveTags") + + for i, v := range options.LoadBalancerNames { + params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v + } + + for i, v := range options.TagKeys { + params["Tags.member."+strconv.Itoa(i+1)+".Key"] = v + } + + resp = &RemoveTagsResp{} + + err = elb.query(params, resp) + + return resp, err +} + +// ---------------------------------------------------------------------------- +// Create + +// The CreateLoadBalancer request parameters +type CreateLoadBalancer struct { + AvailZone []string + Listeners []Listener + LoadBalancerName string + Internal bool // true for vpc elbs + SecurityGroups []string + Subnets []string + Tags []Tag +} + +type CreateLoadBalancerResp struct { + DNSName string `xml:"CreateLoadBalancerResult>DNSName"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) CreateLoadBalancer(options *CreateLoadBalancer) (resp *CreateLoadBalancerResp, err error) { + params := makeParams("CreateLoadBalancer") + + params["LoadBalancerName"] = options.LoadBalancerName + + for i, v := range options.AvailZone { + params["AvailabilityZones.member."+strconv.Itoa(i+1)] = v + } + + for i, v := range options.SecurityGroups { + params["SecurityGroups.member."+strconv.Itoa(i+1)] = v + } + + for i, v := range options.Subnets { + params["Subnets.member."+strconv.Itoa(i+1)] = v + } + + for i, v := range options.Listeners { + params["Listeners.member."+strconv.Itoa(i+1)+".LoadBalancerPort"] = strconv.FormatInt(v.LoadBalancerPort, 10) + params["Listeners.member."+strconv.Itoa(i+1)+".InstancePort"] = strconv.FormatInt(v.InstancePort, 10) + params["Listeners.member."+strconv.Itoa(i+1)+".Protocol"] = v.Protocol + params["Listeners.member."+strconv.Itoa(i+1)+".InstanceProtocol"] = v.InstanceProtocol + params["Listeners.member."+strconv.Itoa(i+1)+".SSLCertificateId"] = v.SSLCertificateId + } + + for i, v := range options.Tags { + params["Tags.member."+strconv.Itoa(i+1)+".Key"] = v.Key + params["Tags.member."+strconv.Itoa(i+1)+".Value"] = v.Value + } + + if options.Internal { + params["Scheme"] = "internal" + } + + resp = &CreateLoadBalancerResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// Destroy + +// The DestroyLoadBalancer request parameters +type DeleteLoadBalancer struct { + LoadBalancerName string +} + +func (elb *ELB) DeleteLoadBalancer(options *DeleteLoadBalancer) (resp *SimpleResp, err error) { + params := makeParams("DeleteLoadBalancer") + + params["LoadBalancerName"] = options.LoadBalancerName + + resp = &SimpleResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// Describe + +// An individual load balancer +type LoadBalancer struct { + LoadBalancerName string `xml:"LoadBalancerName"` + Listeners []Listener `xml:"ListenerDescriptions>member"` + Instances []Instance `xml:"Instances>member"` + HealthCheck HealthCheck `xml:"HealthCheck"` + AvailabilityZones []string `xml:"AvailabilityZones>member"` + HostedZoneNameID string `xml:"CanonicalHostedZoneNameID"` + DNSName string `xml:"DNSName"` + SecurityGroups []string `xml:"SecurityGroups>member"` + Scheme string `xml:"Scheme"` + Subnets []string `xml:"Subnets>member"` +} + +// DescribeLoadBalancer request params +type DescribeLoadBalancer struct { + Names []string +} + +type DescribeLoadBalancersResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` + LoadBalancers []LoadBalancer `xml:"DescribeLoadBalancersResult>LoadBalancerDescriptions>member"` +} + +func (elb *ELB) DescribeLoadBalancers(options *DescribeLoadBalancer) (resp *DescribeLoadBalancersResp, err error) { + params := makeParams("DescribeLoadBalancers") + + for i, v := range options.Names { + params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v + } + + resp = &DescribeLoadBalancersResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// Attributes + +type AccessLog struct { + EmitInterval int64 + Enabled bool + S3BucketName string + S3BucketPrefix string +} + +type ConnectionDraining struct { + Enabled bool + Timeout int64 +} + +type LoadBalancerAttributes struct { + CrossZoneLoadBalancingEnabled bool + ConnectionSettingsIdleTimeout int64 + ConnectionDraining ConnectionDraining + AccessLog AccessLog +} + +type ModifyLoadBalancerAttributes struct { + LoadBalancerName string + LoadBalancerAttributes LoadBalancerAttributes +} + +func (elb *ELB) ModifyLoadBalancerAttributes(options *ModifyLoadBalancerAttributes) (resp *SimpleResp, err error) { + params := makeParams("ModifyLoadBalancerAttributes") + + params["LoadBalancerName"] = options.LoadBalancerName + params["LoadBalancerAttributes.CrossZoneLoadBalancing.Enabled"] = strconv.FormatBool(options.LoadBalancerAttributes.CrossZoneLoadBalancingEnabled) + if options.LoadBalancerAttributes.ConnectionSettingsIdleTimeout > 0 { + params["LoadBalancerAttributes.ConnectionSettings.IdleTimeout"] = strconv.Itoa(int(options.LoadBalancerAttributes.ConnectionSettingsIdleTimeout)) + } + if options.LoadBalancerAttributes.ConnectionDraining.Timeout > 0 { + params["LoadBalancerAttributes.ConnectionDraining.Timeout"] = strconv.Itoa(int(options.LoadBalancerAttributes.ConnectionDraining.Timeout)) + } + params["LoadBalancerAttributes.ConnectionDraining.Enabled"] = strconv.FormatBool(options.LoadBalancerAttributes.ConnectionDraining.Enabled) + params["LoadBalancerAttributes.AccessLog.Enabled"] = strconv.FormatBool(options.LoadBalancerAttributes.AccessLog.Enabled) + if options.LoadBalancerAttributes.AccessLog.Enabled { + params["LoadBalancerAttributes.AccessLog.EmitInterval"] = strconv.Itoa(int(options.LoadBalancerAttributes.AccessLog.EmitInterval)) + params["LoadBalancerAttributes.AccessLog.S3BucketName"] = options.LoadBalancerAttributes.AccessLog.S3BucketName + params["LoadBalancerAttributes.AccessLog.S3BucketPrefix"] = options.LoadBalancerAttributes.AccessLog.S3BucketPrefix + } + + resp = &SimpleResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// Instance Registration / deregistration + +// The RegisterInstancesWithLoadBalancer request parameters +type RegisterInstancesWithLoadBalancer struct { + LoadBalancerName string + Instances []string +} + +type RegisterInstancesWithLoadBalancerResp struct { + Instances []Instance `xml:"RegisterInstancesWithLoadBalancerResult>Instances>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) RegisterInstancesWithLoadBalancer(options *RegisterInstancesWithLoadBalancer) (resp *RegisterInstancesWithLoadBalancerResp, err error) { + params := makeParams("RegisterInstancesWithLoadBalancer") + + params["LoadBalancerName"] = options.LoadBalancerName + + for i, v := range options.Instances { + params["Instances.member."+strconv.Itoa(i+1)+".InstanceId"] = v + } + + resp = &RegisterInstancesWithLoadBalancerResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// The DeregisterInstancesFromLoadBalancer request parameters +type DeregisterInstancesFromLoadBalancer struct { + LoadBalancerName string + Instances []string +} + +type DeregisterInstancesFromLoadBalancerResp struct { + Instances []Instance `xml:"DeregisterInstancesFromLoadBalancerResult>Instances>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) DeregisterInstancesFromLoadBalancer(options *DeregisterInstancesFromLoadBalancer) (resp *DeregisterInstancesFromLoadBalancerResp, err error) { + params := makeParams("DeregisterInstancesFromLoadBalancer") + + params["LoadBalancerName"] = options.LoadBalancerName + + for i, v := range options.Instances { + params["Instances.member."+strconv.Itoa(i+1)+".InstanceId"] = v + } + + resp = &DeregisterInstancesFromLoadBalancerResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// DescribeTags + +type DescribeTags struct { + LoadBalancerNames []string +} + +type LoadBalancerTag struct { + Tags []Tag `xml:"Tags>member"` + LoadBalancerName string `xml:"LoadBalancerName"` +} + +type DescribeTagsResp struct { + LoadBalancerTags []LoadBalancerTag `xml:"DescribeTagsResult>TagDescriptions>member"` + NextToken string `xml:"DescribeTagsResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) DescribeTags(options *DescribeTags) (resp *DescribeTagsResp, err error) { + params := makeParams("DescribeTags") + + for i, v := range options.LoadBalancerNames { + params["LoadBalancerNames.member."+strconv.Itoa(i+1)] = v + } + + resp = &DescribeTagsResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// Health Checks + +type HealthCheck struct { + HealthyThreshold int64 `xml:"HealthyThreshold"` + UnhealthyThreshold int64 `xml:"UnhealthyThreshold"` + Interval int64 `xml:"Interval"` + Target string `xml:"Target"` + Timeout int64 `xml:"Timeout"` +} + +type ConfigureHealthCheck struct { + LoadBalancerName string + Check HealthCheck +} + +type ConfigureHealthCheckResp struct { + Check HealthCheck `xml:"ConfigureHealthCheckResult>HealthCheck"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) ConfigureHealthCheck(options *ConfigureHealthCheck) (resp *ConfigureHealthCheckResp, err error) { + params := makeParams("ConfigureHealthCheck") + + params["LoadBalancerName"] = options.LoadBalancerName + params["HealthCheck.HealthyThreshold"] = strconv.Itoa(int(options.Check.HealthyThreshold)) + params["HealthCheck.UnhealthyThreshold"] = strconv.Itoa(int(options.Check.UnhealthyThreshold)) + params["HealthCheck.Interval"] = strconv.Itoa(int(options.Check.Interval)) + params["HealthCheck.Target"] = options.Check.Target + params["HealthCheck.Timeout"] = strconv.Itoa(int(options.Check.Timeout)) + + resp = &ConfigureHealthCheckResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// ---------------------------------------------------------------------------- +// Instance Health + +// The DescribeInstanceHealth request parameters +type DescribeInstanceHealth struct { + LoadBalancerName string +} + +type DescribeInstanceHealthResp struct { + InstanceStates []InstanceState `xml:"DescribeInstanceHealthResult>InstanceStates>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +func (elb *ELB) DescribeInstanceHealth(options *DescribeInstanceHealth) (resp *DescribeInstanceHealthResp, err error) { + params := makeParams("DescribeInstanceHealth") + + params["LoadBalancerName"] = options.LoadBalancerName + + resp = &DescribeInstanceHealthResp{} + + err = elb.query(params, resp) + + if err != nil { + resp = nil + } + + return +} + +// Responses + +type SimpleResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +type xmlErrors struct { + Errors []Error `xml:"Error"` +} + +// Error encapsulates an elb error. +type Error struct { + // HTTP status code of the error. + StatusCode int + + // AWS code of the error. + Code string + + // Message explaining the error. + Message string +} + +func (e *Error) Error() string { + var prefix string + if e.Code != "" { + prefix = e.Code + ": " + } + if prefix == "" && e.StatusCode > 0 { + prefix = strconv.Itoa(e.StatusCode) + ": " + } + return prefix + e.Message +} diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb_test.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb_test.go new file mode 100644 index 00000000000..d0a5caab618 --- /dev/null +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/elb_test.go @@ -0,0 +1,235 @@ +package elb_test + +import ( + "github.com/mitchellh/goamz/aws" + "github.com/mitchellh/goamz/elb" + "github.com/mitchellh/goamz/testutil" + . "github.com/motain/gocheck" + "testing" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type S struct { + elb *elb.ELB +} + +var _ = Suite(&S{}) + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{"abc", "123", ""} + s.elb = elb.NewWithClient(auth, aws.Region{ELBEndpoint: testServer.URL}, testutil.DefaultClient) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestAddTags(c *C) { + testServer.Response(200, nil, AddTagsExample) + + options := elb.AddTags{ + LoadBalancerNames: []string{"foobar"}, + Tags: []elb.Tag{ + { + Key: "hello", + Value: "world", + }, + }, + } + + resp, err := s.elb.AddTags(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"AddTags"}) + c.Assert(req.Form["LoadBalancerNames.member.1"], DeepEquals, []string{"foobar"}) + c.Assert(req.Form["Tags.member.1.Key"], DeepEquals, []string{"hello"}) + c.Assert(req.Form["Tags.member.1.Value"], DeepEquals, []string{"world"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "360e81f7-1100-11e4-b6ed-0f30EXAMPLE") +} + +func (s *S) TestRemoveTags(c *C) { + testServer.Response(200, nil, RemoveTagsExample) + + options := elb.RemoveTags{ + LoadBalancerNames: []string{"foobar"}, + TagKeys: []string{"hello"}, + } + + resp, err := s.elb.RemoveTags(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"RemoveTags"}) + c.Assert(req.Form["LoadBalancerNames.member.1"], DeepEquals, []string{"foobar"}) + c.Assert(req.Form["Tags.member.1.Key"], DeepEquals, []string{"hello"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE") +} + +func (s *S) TestCreateLoadBalancer(c *C) { + testServer.Response(200, nil, CreateLoadBalancerExample) + + options := elb.CreateLoadBalancer{ + AvailZone: []string{"us-east-1a"}, + Listeners: []elb.Listener{elb.Listener{ + InstancePort: 80, + InstanceProtocol: "http", + SSLCertificateId: "needToAddASSLCertToYourAWSAccount", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + LoadBalancerName: "foobar", + Internal: false, + SecurityGroups: []string{"sg1"}, + Subnets: []string{"sn1"}, + } + + resp, err := s.elb.CreateLoadBalancer(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateLoadBalancer"}) + c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "1549581b-12b7-11e3-895e-1334aEXAMPLE") +} + +func (s *S) TestDeleteLoadBalancer(c *C) { + testServer.Response(200, nil, DeleteLoadBalancerExample) + + options := elb.DeleteLoadBalancer{ + LoadBalancerName: "foobar", + } + + resp, err := s.elb.DeleteLoadBalancer(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteLoadBalancer"}) + c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "1549581b-12b7-11e3-895e-1334aEXAMPLE") +} + +func (s *S) TestDescribeLoadBalancers(c *C) { + testServer.Response(200, nil, DescribeLoadBalancersExample) + + options := elb.DescribeLoadBalancer{ + Names: []string{"foobar"}, + } + + resp, err := s.elb.DescribeLoadBalancers(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeLoadBalancers"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE") + c.Assert(resp.LoadBalancers[0].LoadBalancerName, Equals, "MyLoadBalancer") + c.Assert(resp.LoadBalancers[0].Listeners[0].Protocol, Equals, "HTTP") + c.Assert(resp.LoadBalancers[0].Instances[0].InstanceId, Equals, "i-e4cbe38d") + c.Assert(resp.LoadBalancers[0].AvailabilityZones[0].AvailabilityZone, Equals, "us-east-1a") + c.Assert(resp.LoadBalancers[0].Scheme, Equals, "internet-facing") + c.Assert(resp.LoadBalancers[0].DNSName, Equals, "MyLoadBalancer-123456789.us-east-1.elb.amazonaws.com") + c.Assert(resp.LoadBalancers[0].HealthCheck.HealthyThreshold, Equals, int64(2)) + c.Assert(resp.LoadBalancers[0].HealthCheck.UnhealthyThreshold, Equals, int64(10)) + c.Assert(resp.LoadBalancers[0].HealthCheck.Interval, Equals, int64(90)) + c.Assert(resp.LoadBalancers[0].HealthCheck.Target, Equals, "HTTP:80/") + c.Assert(resp.LoadBalancers[0].HealthCheck.Timeout, Equals, int64(60)) +} + +func (s *S) TestRegisterInstancesWithLoadBalancer(c *C) { + testServer.Response(200, nil, RegisterInstancesWithLoadBalancerExample) + + options := elb.RegisterInstancesWithLoadBalancer{ + LoadBalancerName: "foobar", + Instances: []string{"instance-1", "instance-2"}, + } + + resp, err := s.elb.RegisterInstancesWithLoadBalancer(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"RegisterInstancesWithLoadBalancer"}) + c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"}) + c.Assert(req.Form["Instances.member.1.InstanceId"], DeepEquals, []string{"instance-1"}) + c.Assert(req.Form["Instances.member.2.InstanceId"], DeepEquals, []string{"instance-2"}) + c.Assert(err, IsNil) + + c.Assert(resp.Instances[0].InstanceId, Equals, "i-315b7e51") + c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE") +} + +func (s *S) TestDeregisterInstancesFromLoadBalancer(c *C) { + testServer.Response(200, nil, DeregisterInstancesFromLoadBalancerExample) + + options := elb.DeregisterInstancesFromLoadBalancer{ + LoadBalancerName: "foobar", + Instances: []string{"instance-1", "instance-2"}, + } + + resp, err := s.elb.DeregisterInstancesFromLoadBalancer(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"DeregisterInstancesFromLoadBalancer"}) + c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"}) + c.Assert(req.Form["Instances.member.1.InstanceId"], DeepEquals, []string{"instance-1"}) + c.Assert(req.Form["Instances.member.2.InstanceId"], DeepEquals, []string{"instance-2"}) + c.Assert(err, IsNil) + + c.Assert(resp.Instances[0].InstanceId, Equals, "i-6ec63d59") + c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE") +} + +func (s *S) TestConfigureHealthCheck(c *C) { + testServer.Response(200, nil, ConfigureHealthCheckExample) + + options := elb.ConfigureHealthCheck{ + LoadBalancerName: "foobar", + Check: elb.HealthCheck{ + HealthyThreshold: 2, + UnhealthyThreshold: 2, + Interval: 30, + Target: "HTTP:80/ping", + Timeout: 3, + }, + } + + resp, err := s.elb.ConfigureHealthCheck(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"ConfigureHealthCheck"}) + c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"}) + c.Assert(err, IsNil) + + c.Assert(resp.Check.HealthyThreshold, Equals, int64(2)) + c.Assert(resp.Check.UnhealthyThreshold, Equals, int64(2)) + c.Assert(resp.Check.Interval, Equals, int64(30)) + c.Assert(resp.Check.Target, Equals, "HTTP:80/ping") + c.Assert(resp.Check.Timeout, Equals, int64(3)) + c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12EXAMPLE") +} + +func (s *S) TestDescribeInstanceHealth(c *C) { + testServer.Response(200, nil, DescribeInstanceHealthExample) + + options := elb.DescribeInstanceHealth{ + LoadBalancerName: "foobar", + } + + resp, err := s.elb.DescribeInstanceHealth(&options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstanceHealth"}) + c.Assert(req.Form["LoadBalancerName"], DeepEquals, []string{"foobar"}) + c.Assert(err, IsNil) + + c.Assert(resp.InstanceStates[0].InstanceId, Equals, "i-90d8c2a5") + c.Assert(resp.InstanceStates[0].State, Equals, "InService") + c.Assert(resp.InstanceStates[1].InstanceId, Equals, "i-06ea3e60") + c.Assert(resp.InstanceStates[1].State, Equals, "OutOfService") + c.Assert(resp.RequestId, Equals, "1549581b-12b7-11e3-895e-1334aEXAMPLE") +} diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/responses_test.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/responses_test.go new file mode 100644 index 00000000000..77ae390cec7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/responses_test.go @@ -0,0 +1,182 @@ +package elb_test + +var ErrorDump = ` + +UnsupportedOperation + +0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4 +` + +// http://goo.gl/OkMdtJ +var AddTagsExample = ` + + + + 360e81f7-1100-11e4-b6ed-0f30EXAMPLE + + +` + +// http://goo.gl/nT2E89 +var RemoveTagsExample = ` + + + + 83c88b9d-12b7-11e3-8b82-87b12EXAMPLE + + +` + +// http://goo.gl/gQRD2H +var CreateLoadBalancerExample = ` + + + MyLoadBalancer-1234567890.us-east-1.elb.amazonaws.com + + + 1549581b-12b7-11e3-895e-1334aEXAMPLE + + +` + +// http://goo.gl/GLZeBN +var DeleteLoadBalancerExample = ` + + + 1549581b-12b7-11e3-895e-1334aEXAMPLE + + +` + +// http://goo.gl/8UgpQ8 +var DescribeLoadBalancersExample = ` + + + + + + MyLoadBalancer + 2013-05-24T21:15:31.280Z + + 90 + HTTP:80/ + 2 + 60 + 10 + + + + + + HTTP + 80 + HTTP + needToAddASSLCertToYourAWSAccount + 80 + + + + + + i-e4cbe38d + + + + + + + + + us-east-1a + + ZZZZZZZZZZZ123X + MyLoadBalancer-123456789.us-east-1.elb.amazonaws.com + internet-facing + + amazon-elb + amazon-elb-sg + + MyLoadBalancer-123456789.us-east-1.elb.amazonaws.com + + + + + + + 83c88b9d-12b7-11e3-8b82-87b12EXAMPLE + + +` + +// http://goo.gl/Uz1N66 +var RegisterInstancesWithLoadBalancerExample = ` + + + + + i-315b7e51 + + + + + 83c88b9d-12b7-11e3-8b82-87b12EXAMPLE + + + ` + +// http://goo.gl/5OMv62 +var DeregisterInstancesFromLoadBalancerExample = ` + + + + + i-6ec63d59 + + + + + 83c88b9d-12b7-11e3-8b82-87b12EXAMPLE + + +` + +// http://docs.aws.amazon.com/ElasticLoadBalancing/latest/APIReference/API_ConfigureHealthCheck.html +var ConfigureHealthCheckExample = ` + + + + 30 + HTTP:80/ping + 2 + 3 + 2 + + + + 83c88b9d-12b7-11e3-8b82-87b12EXAMPLE + +` + +// http://goo.gl/cGNxfj +var DescribeInstanceHealthExample = ` + + + + + N/A + i-90d8c2a5 + InService + N/A + + + N/A + i-06ea3e60 + OutOfService + N/A + + + + + 1549581b-12b7-11e3-895e-1334aEXAMPLE + +` diff --git a/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/sign.go b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/sign.go new file mode 100644 index 00000000000..06310304dd2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/mitchellh/goamz/elb/sign.go @@ -0,0 +1,38 @@ +package elb + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/mitchellh/goamz/aws" + "sort" + "strings" +) + +// ---------------------------------------------------------------------------- +// Version 2 signing (http://goo.gl/RSRp5) + +var b64 = base64.StdEncoding + +func sign(auth aws.Auth, method, path string, params map[string]string, host string) { + params["AWSAccessKeyId"] = auth.AccessKey + params["SignatureVersion"] = "2" + params["SignatureMethod"] = "HmacSHA256" + if auth.Token != "" { + params["SecurityToken"] = auth.Token + } + + var sarray []string + for k, v := range params { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v)) + } + sort.StringSlice(sarray).Sort() + joined := strings.Join(sarray, "&") + payload := method + "\n" + host + "\n" + path + "\n" + joined + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + params["Signature"] = string(signature) +}