|
- // This package provides types and functions to interact Elastic Load Balancing service
- package elb
-
- import (
- "encoding/xml"
- "fmt"
- "net/http"
- "net/url"
- "sort"
- "strconv"
- "strings"
- "time"
-
- "github.com/goamz/goamz/aws"
- )
-
- type ELB struct {
- aws.Auth
- aws.Region
- }
-
- func New(auth aws.Auth, region aws.Region) *ELB {
- return &ELB{auth, region}
- }
-
- // The CreateLoadBalancer type encapsulates options for the respective request in AWS.
- // The creation of a Load Balancer may differ inside EC2 and VPC.
- //
- // See http://goo.gl/4QFKi for more details.
- type CreateLoadBalancer struct {
- Name string
- AvailabilityZones []string
- Listeners []Listener
- Scheme string
- SecurityGroups []string
- Subnets []string
- }
-
- // Listener to configure in Load Balancer.
- //
- // See http://goo.gl/NJQCj for more details.
- type Listener struct {
- InstancePort int
- InstanceProtocol string
- LoadBalancerPort int
- Protocol string
- SSLCertificateId string
- }
-
- // Response to a CreateLoadBalance request.
- //
- // See http://goo.gl/4QFKi for more details.
- type CreateLoadBalancerResp struct {
- DNSName string `xml:"CreateLoadBalancerResult>DNSName"`
- }
-
- type SimpleResp struct {
- RequestId string `xml:"ResponseMetadata>RequestId"`
- }
-
- // Creates a Load Balancer in Amazon.
- //
- // See http://goo.gl/4QFKi for more details.
- func (elb *ELB) CreateLoadBalancer(options *CreateLoadBalancer) (resp *CreateLoadBalancerResp, err error) {
- params := makeCreateParams(options)
- resp = new(CreateLoadBalancerResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return
- }
-
- // Deletes a Load Balancer.
- //
- // See http://goo.gl/sDmPp for more details.
- func (elb *ELB) DeleteLoadBalancer(name string) (resp *SimpleResp, err error) {
- params := map[string]string{
- "Action": "DeleteLoadBalancer",
- "LoadBalancerName": name,
- }
- resp = new(SimpleResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return resp, nil
- }
-
- type RegisterInstancesResp struct {
- InstanceIds []string `xml:"RegisterInstancesWithLoadBalancerResult>Instances>member>InstanceId"`
- }
-
- // Register N instances with a given Load Balancer.
- //
- // See http://goo.gl/x9hru for more details.
- func (elb *ELB) RegisterInstancesWithLoadBalancer(instanceIds []string, lbName string) (resp *RegisterInstancesResp, err error) {
- // TODO: change params order and use ..., e.g (lbName string, instanceIds ...string)
- params := map[string]string{
- "Action": "RegisterInstancesWithLoadBalancer",
- "LoadBalancerName": lbName,
- }
- for i, instanceId := range instanceIds {
- key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
- params[key] = instanceId
- }
- resp = new(RegisterInstancesResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return resp, nil
- }
-
- // Deregister N instances from a given Load Balancer.
- //
- // See http://goo.gl/Hgo4U for more details.
- func (elb *ELB) DeregisterInstancesFromLoadBalancer(instanceIds []string, lbName string) (resp *SimpleResp, err error) {
- // TODO: change params order and use ..., e.g (lbName string, instanceIds ...string)
- params := map[string]string{
- "Action": "DeregisterInstancesFromLoadBalancer",
- "LoadBalancerName": lbName,
- }
- for i, instanceId := range instanceIds {
- key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
- params[key] = instanceId
- }
- resp = new(SimpleResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return resp, nil
- }
-
- type DescribeLoadBalancerResp struct {
- LoadBalancerDescriptions []LoadBalancerDescription `xml:"DescribeLoadBalancersResult>LoadBalancerDescriptions>member"`
- }
-
- type LoadBalancerDescription struct {
- AvailabilityZones []string `xml:"AvailabilityZones>member"`
- BackendServerDescriptions []BackendServerDescriptions `xml:"BackendServerDescriptions>member"`
- CanonicalHostedZoneName string `xml:"CanonicalHostedZoneName"`
- CanonicalHostedZoneNameId string `xml:"CanonicalHostedZoneNameID"`
- CreatedTime time.Time `xml:"CreatedTime"`
- DNSName string `xml:"DNSName"`
- HealthCheck HealthCheck `xml:"HealthCheck"`
- Instances []Instance `xml:"Instances>member"`
- ListenerDescriptions []ListenerDescription `xml:"ListenerDescriptions>member"`
- LoadBalancerName string `xml:"LoadBalancerName"`
- Policies Policies `xml:"Policies"`
- Scheme string `xml:"Scheme"`
- SecurityGroups []string `xml:"SecurityGroups>member"` //vpc only
- SourceSecurityGroup SourceSecurityGroup `xml:"SourceSecurityGroup"`
- Subnets []string `xml:"Subnets>member"`
- VPCId string `xml:"VPCId"`
- }
-
- // Describe Load Balancers.
- // It can be used to describe all Load Balancers or specific ones.
- //
- // See http://goo.gl/wofJA for more details.
- func (elb *ELB) DescribeLoadBalancers(names ...string) (*DescribeLoadBalancerResp, error) {
- params := map[string]string{"Action": "DescribeLoadBalancers"}
- for i, name := range names {
- index := fmt.Sprintf("LoadBalancerNames.member.%d", i+1)
- params[index] = name
- }
- resp := new(DescribeLoadBalancerResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return resp, nil
- }
-
- type BackendServerDescriptions struct {
- InstancePort int `xml:"InstancePort"`
- PolicyNames []string `xml:"PolicyNames>member"`
- }
-
- type HealthCheck struct {
- HealthyThreshold int `xml:"HealthyThreshold"`
- Interval int `xml:"Interval"`
- Target string `xml:"Target"`
- Timeout int `xml:"Timeout"`
- UnhealthyThreshold int `xml:"UnhealthyThreshold"`
- }
-
- type Instance struct {
- InstanceId string `xml:"InstanceId"`
- }
-
- type ListenerDescription struct {
- Listener Listener `xml:"Listener"`
- PolicyNames []string `xml:"PolicyNames>member"`
- }
-
- type Policies struct {
- AppCookieStickinessPolicies []AppCookieStickinessPolicies `xml:"AppCookieStickinessPolicies>member"`
- LBCookieStickinessPolicies []LBCookieStickinessPolicies `xml:"LBCookieStickinessPolicies>member"`
- OtherPolicies []string `xml:"OtherPolicies>member"`
- }
-
- // see http://goo.gl/clXGV for more information.
- type AppCookieStickinessPolicies struct {
- CookieName string `xml:"CookieName"`
- PolicyName string `xml:"PolicyName"`
- }
-
- type LBCookieStickinessPolicies struct {
- CookieExpirationPeriod int `xml:"CookieExpirationPeriod"`
- PolicyName string `xml:"PolicyName"`
- }
-
- type SourceSecurityGroup struct {
- GroupName string `xml:"GroupName"`
- OwnerAlias string `xml:"OwnerAlias"`
- }
-
- // Represents a XML response for DescribeInstanceHealth action
- //
- // See http://goo.gl/ovIB1 for more information.
- type DescribeInstanceHealthResp struct {
- InstanceStates []InstanceState `xml:"DescribeInstanceHealthResult>InstanceStates>member"`
- }
-
- // See http://goo.gl/dzWfP for more information.
- type InstanceState struct {
- Description string `xml:"Description"`
- InstanceId string `xml:"InstanceId"`
- ReasonCode string `xml:"ReasonCode"`
- State string `xml:"State"`
- }
-
- // Describe instance health.
- //
- // See http://goo.gl/ovIB1 for more information.
- func (elb *ELB) DescribeInstanceHealth(lbName string, instanceIds ...string) (*DescribeInstanceHealthResp, error) {
- params := map[string]string{
- "Action": "DescribeInstanceHealth",
- "LoadBalancerName": lbName,
- }
- for i, iId := range instanceIds {
- key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
- params[key] = iId
- }
- resp := new(DescribeInstanceHealthResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return resp, nil
- }
-
- type HealthCheckResp struct {
- HealthCheck *HealthCheck `xml:"ConfigureHealthCheckResult>HealthCheck"`
- }
-
- // Configure health check for a LB
- //
- // See http://goo.gl/2HE6a for more information
- func (elb *ELB) ConfigureHealthCheck(lbName string, healthCheck *HealthCheck) (*HealthCheckResp, error) {
- params := map[string]string{
- "Action": "ConfigureHealthCheck",
- "LoadBalancerName": lbName,
- "HealthCheck.HealthyThreshold": strconv.Itoa(healthCheck.HealthyThreshold),
- "HealthCheck.Interval": strconv.Itoa(healthCheck.Interval),
- "HealthCheck.Target": healthCheck.Target,
- "HealthCheck.Timeout": strconv.Itoa(healthCheck.Timeout),
- "HealthCheck.UnhealthyThreshold": strconv.Itoa(healthCheck.UnhealthyThreshold),
- }
- resp := new(HealthCheckResp)
- if err := elb.query(params, resp); err != nil {
- return nil, err
- }
- return resp, nil
- }
-
- // Add tags to the named ELB
- //
- // Note that AWS only accepts one ELB name at a time (even though it is sent as a list)
- //
- // See http://goo.gl/6JW4Wf for the rest of the details
- func (elb *ELB) AddTags(elbName string, tags map[string]string) (*SimpleResp, error) {
- var sortedKeys []string
- params := make(map[string]string)
- response := &SimpleResp{}
-
- for tagKey := range tags {
- sortedKeys = append(sortedKeys, tagKey)
- }
-
- sort.Strings(sortedKeys)
-
- for _, key := range sortedKeys {
- number := len(tags)
- params[fmt.Sprintf("Tags.member.%d.Key", number)] = key
- params[fmt.Sprintf("Tags.member.%d.Value", number)] = tags[key]
- delete(tags, key)
- }
-
- params["Action"] = "AddTags"
- params["LoadBalancerNames.member.1"] = elbName
-
- if err := elb.query(params, response); err != nil {
- return nil, err
- }
-
- return response, nil
- }
-
- // Remove tags from the named ELB
- //
- // Note that AWS only accepts one ELB name at a time (even though it is sent as a list)
- //
- // see http://goo.gl/ochFqo for more details
-
- func (elb *ELB) RemoveTags(elbName string, tagKeys []string) (*SimpleResp, error) {
- response := &SimpleResp{}
- params := make(map[string]string)
-
- params["Action"] = "RemoveTags"
- params["LoadBalancerNames.member.1"] = elbName
-
- for i, tagKey := range tagKeys {
- params[fmt.Sprintf("Tags.member.%d.Key", i+1)] = tagKey
- }
-
- if err := elb.query(params, response); err != nil {
- return nil, err
- }
-
- return response, nil
- }
-
- func (elb *ELB) query(params map[string]string, resp interface{}) error {
- params["Version"] = "2012-06-01"
- params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339)
- data := strings.NewReader(multimap(params).Encode())
- hreq, err := http.NewRequest("GET", elb.Region.ELBEndpoint+"/", data)
- if err != nil {
- return err
- }
-
- hreq.URL.RawQuery = multimap(params).Encode()
- token := elb.Auth.Token()
- if token != "" {
- hreq.Header.Set("X-Amz-Security-Token", token)
- }
-
- signer := aws.NewV4Signer(elb.Auth, "elasticloadbalancing", elb.Region)
- signer.Sign(hreq)
-
- r, err := http.DefaultClient.Do(hreq)
-
- if err != nil {
- return err
- }
- defer r.Body.Close()
- if r.StatusCode != 200 {
- return buildError(r)
- }
- return xml.NewDecoder(r.Body).Decode(resp)
- }
-
- // Error encapsulates an error returned by ELB.
- type Error struct {
- // HTTP status code
- StatusCode int
- // AWS error code
- Code string
- // The human-oriented error message
- Message string
- }
-
- func (err *Error) Error() string {
- if err.Code == "" {
- return err.Message
- }
-
- return fmt.Sprintf("%s (%s)", err.Message, err.Code)
- }
-
- type xmlErrors struct {
- Errors []Error `xml:"Error"`
- }
-
- 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 makeCreateParams(createLB *CreateLoadBalancer) map[string]string {
- params := make(map[string]string)
- params["LoadBalancerName"] = createLB.Name
- params["Action"] = "CreateLoadBalancer"
- if createLB.Scheme != "" {
- params["Scheme"] = createLB.Scheme
- }
- for i, s := range createLB.SecurityGroups {
- key := fmt.Sprintf("SecurityGroups.member.%d", i+1)
- params[key] = s
- }
- for i, s := range createLB.Subnets {
- key := fmt.Sprintf("Subnets.member.%d", i+1)
- params[key] = s
- }
- for i, l := range createLB.Listeners {
- key := "Listeners.member.%d.%s"
- index := i + 1
- params[fmt.Sprintf(key, index, "InstancePort")] = strconv.Itoa(l.InstancePort)
- params[fmt.Sprintf(key, index, "InstanceProtocol")] = l.InstanceProtocol
- params[fmt.Sprintf(key, index, "Protocol")] = l.Protocol
- params[fmt.Sprintf(key, index, "LoadBalancerPort")] = strconv.Itoa(l.LoadBalancerPort)
- }
- for i, az := range createLB.AvailabilityZones {
- key := fmt.Sprintf("AvailabilityZones.member.%d", i+1)
- params[key] = az
- }
- return params
- }
|