Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

436 строки
13 KiB

  1. // This package provides types and functions to interact Elastic Load Balancing service
  2. package elb
  3. import (
  4. "encoding/xml"
  5. "fmt"
  6. "net/http"
  7. "net/url"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/goamz/goamz/aws"
  13. )
  14. type ELB struct {
  15. aws.Auth
  16. aws.Region
  17. }
  18. func New(auth aws.Auth, region aws.Region) *ELB {
  19. return &ELB{auth, region}
  20. }
  21. // The CreateLoadBalancer type encapsulates options for the respective request in AWS.
  22. // The creation of a Load Balancer may differ inside EC2 and VPC.
  23. //
  24. // See http://goo.gl/4QFKi for more details.
  25. type CreateLoadBalancer struct {
  26. Name string
  27. AvailabilityZones []string
  28. Listeners []Listener
  29. Scheme string
  30. SecurityGroups []string
  31. Subnets []string
  32. }
  33. // Listener to configure in Load Balancer.
  34. //
  35. // See http://goo.gl/NJQCj for more details.
  36. type Listener struct {
  37. InstancePort int
  38. InstanceProtocol string
  39. LoadBalancerPort int
  40. Protocol string
  41. SSLCertificateId string
  42. }
  43. // Response to a CreateLoadBalance request.
  44. //
  45. // See http://goo.gl/4QFKi for more details.
  46. type CreateLoadBalancerResp struct {
  47. DNSName string `xml:"CreateLoadBalancerResult>DNSName"`
  48. }
  49. type SimpleResp struct {
  50. RequestId string `xml:"ResponseMetadata>RequestId"`
  51. }
  52. // Creates a Load Balancer in Amazon.
  53. //
  54. // See http://goo.gl/4QFKi for more details.
  55. func (elb *ELB) CreateLoadBalancer(options *CreateLoadBalancer) (resp *CreateLoadBalancerResp, err error) {
  56. params := makeCreateParams(options)
  57. resp = new(CreateLoadBalancerResp)
  58. if err := elb.query(params, resp); err != nil {
  59. return nil, err
  60. }
  61. return
  62. }
  63. // Deletes a Load Balancer.
  64. //
  65. // See http://goo.gl/sDmPp for more details.
  66. func (elb *ELB) DeleteLoadBalancer(name string) (resp *SimpleResp, err error) {
  67. params := map[string]string{
  68. "Action": "DeleteLoadBalancer",
  69. "LoadBalancerName": name,
  70. }
  71. resp = new(SimpleResp)
  72. if err := elb.query(params, resp); err != nil {
  73. return nil, err
  74. }
  75. return resp, nil
  76. }
  77. type RegisterInstancesResp struct {
  78. InstanceIds []string `xml:"RegisterInstancesWithLoadBalancerResult>Instances>member>InstanceId"`
  79. }
  80. // Register N instances with a given Load Balancer.
  81. //
  82. // See http://goo.gl/x9hru for more details.
  83. func (elb *ELB) RegisterInstancesWithLoadBalancer(instanceIds []string, lbName string) (resp *RegisterInstancesResp, err error) {
  84. // TODO: change params order and use ..., e.g (lbName string, instanceIds ...string)
  85. params := map[string]string{
  86. "Action": "RegisterInstancesWithLoadBalancer",
  87. "LoadBalancerName": lbName,
  88. }
  89. for i, instanceId := range instanceIds {
  90. key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
  91. params[key] = instanceId
  92. }
  93. resp = new(RegisterInstancesResp)
  94. if err := elb.query(params, resp); err != nil {
  95. return nil, err
  96. }
  97. return resp, nil
  98. }
  99. // Deregister N instances from a given Load Balancer.
  100. //
  101. // See http://goo.gl/Hgo4U for more details.
  102. func (elb *ELB) DeregisterInstancesFromLoadBalancer(instanceIds []string, lbName string) (resp *SimpleResp, err error) {
  103. // TODO: change params order and use ..., e.g (lbName string, instanceIds ...string)
  104. params := map[string]string{
  105. "Action": "DeregisterInstancesFromLoadBalancer",
  106. "LoadBalancerName": lbName,
  107. }
  108. for i, instanceId := range instanceIds {
  109. key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
  110. params[key] = instanceId
  111. }
  112. resp = new(SimpleResp)
  113. if err := elb.query(params, resp); err != nil {
  114. return nil, err
  115. }
  116. return resp, nil
  117. }
  118. type DescribeLoadBalancerResp struct {
  119. LoadBalancerDescriptions []LoadBalancerDescription `xml:"DescribeLoadBalancersResult>LoadBalancerDescriptions>member"`
  120. }
  121. type LoadBalancerDescription struct {
  122. AvailabilityZones []string `xml:"AvailabilityZones>member"`
  123. BackendServerDescriptions []BackendServerDescriptions `xml:"BackendServerDescriptions>member"`
  124. CanonicalHostedZoneName string `xml:"CanonicalHostedZoneName"`
  125. CanonicalHostedZoneNameId string `xml:"CanonicalHostedZoneNameID"`
  126. CreatedTime time.Time `xml:"CreatedTime"`
  127. DNSName string `xml:"DNSName"`
  128. HealthCheck HealthCheck `xml:"HealthCheck"`
  129. Instances []Instance `xml:"Instances>member"`
  130. ListenerDescriptions []ListenerDescription `xml:"ListenerDescriptions>member"`
  131. LoadBalancerName string `xml:"LoadBalancerName"`
  132. Policies Policies `xml:"Policies"`
  133. Scheme string `xml:"Scheme"`
  134. SecurityGroups []string `xml:"SecurityGroups>member"` //vpc only
  135. SourceSecurityGroup SourceSecurityGroup `xml:"SourceSecurityGroup"`
  136. Subnets []string `xml:"Subnets>member"`
  137. VPCId string `xml:"VPCId"`
  138. }
  139. // Describe Load Balancers.
  140. // It can be used to describe all Load Balancers or specific ones.
  141. //
  142. // See http://goo.gl/wofJA for more details.
  143. func (elb *ELB) DescribeLoadBalancers(names ...string) (*DescribeLoadBalancerResp, error) {
  144. params := map[string]string{"Action": "DescribeLoadBalancers"}
  145. for i, name := range names {
  146. index := fmt.Sprintf("LoadBalancerNames.member.%d", i+1)
  147. params[index] = name
  148. }
  149. resp := new(DescribeLoadBalancerResp)
  150. if err := elb.query(params, resp); err != nil {
  151. return nil, err
  152. }
  153. return resp, nil
  154. }
  155. type BackendServerDescriptions struct {
  156. InstancePort int `xml:"InstancePort"`
  157. PolicyNames []string `xml:"PolicyNames>member"`
  158. }
  159. type HealthCheck struct {
  160. HealthyThreshold int `xml:"HealthyThreshold"`
  161. Interval int `xml:"Interval"`
  162. Target string `xml:"Target"`
  163. Timeout int `xml:"Timeout"`
  164. UnhealthyThreshold int `xml:"UnhealthyThreshold"`
  165. }
  166. type Instance struct {
  167. InstanceId string `xml:"InstanceId"`
  168. }
  169. type ListenerDescription struct {
  170. Listener Listener `xml:"Listener"`
  171. PolicyNames []string `xml:"PolicyNames>member"`
  172. }
  173. type Policies struct {
  174. AppCookieStickinessPolicies []AppCookieStickinessPolicies `xml:"AppCookieStickinessPolicies>member"`
  175. LBCookieStickinessPolicies []LBCookieStickinessPolicies `xml:"LBCookieStickinessPolicies>member"`
  176. OtherPolicies []string `xml:"OtherPolicies>member"`
  177. }
  178. // see http://goo.gl/clXGV for more information.
  179. type AppCookieStickinessPolicies struct {
  180. CookieName string `xml:"CookieName"`
  181. PolicyName string `xml:"PolicyName"`
  182. }
  183. type LBCookieStickinessPolicies struct {
  184. CookieExpirationPeriod int `xml:"CookieExpirationPeriod"`
  185. PolicyName string `xml:"PolicyName"`
  186. }
  187. type SourceSecurityGroup struct {
  188. GroupName string `xml:"GroupName"`
  189. OwnerAlias string `xml:"OwnerAlias"`
  190. }
  191. // Represents a XML response for DescribeInstanceHealth action
  192. //
  193. // See http://goo.gl/ovIB1 for more information.
  194. type DescribeInstanceHealthResp struct {
  195. InstanceStates []InstanceState `xml:"DescribeInstanceHealthResult>InstanceStates>member"`
  196. }
  197. // See http://goo.gl/dzWfP for more information.
  198. type InstanceState struct {
  199. Description string `xml:"Description"`
  200. InstanceId string `xml:"InstanceId"`
  201. ReasonCode string `xml:"ReasonCode"`
  202. State string `xml:"State"`
  203. }
  204. // Describe instance health.
  205. //
  206. // See http://goo.gl/ovIB1 for more information.
  207. func (elb *ELB) DescribeInstanceHealth(lbName string, instanceIds ...string) (*DescribeInstanceHealthResp, error) {
  208. params := map[string]string{
  209. "Action": "DescribeInstanceHealth",
  210. "LoadBalancerName": lbName,
  211. }
  212. for i, iId := range instanceIds {
  213. key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
  214. params[key] = iId
  215. }
  216. resp := new(DescribeInstanceHealthResp)
  217. if err := elb.query(params, resp); err != nil {
  218. return nil, err
  219. }
  220. return resp, nil
  221. }
  222. type HealthCheckResp struct {
  223. HealthCheck *HealthCheck `xml:"ConfigureHealthCheckResult>HealthCheck"`
  224. }
  225. // Configure health check for a LB
  226. //
  227. // See http://goo.gl/2HE6a for more information
  228. func (elb *ELB) ConfigureHealthCheck(lbName string, healthCheck *HealthCheck) (*HealthCheckResp, error) {
  229. params := map[string]string{
  230. "Action": "ConfigureHealthCheck",
  231. "LoadBalancerName": lbName,
  232. "HealthCheck.HealthyThreshold": strconv.Itoa(healthCheck.HealthyThreshold),
  233. "HealthCheck.Interval": strconv.Itoa(healthCheck.Interval),
  234. "HealthCheck.Target": healthCheck.Target,
  235. "HealthCheck.Timeout": strconv.Itoa(healthCheck.Timeout),
  236. "HealthCheck.UnhealthyThreshold": strconv.Itoa(healthCheck.UnhealthyThreshold),
  237. }
  238. resp := new(HealthCheckResp)
  239. if err := elb.query(params, resp); err != nil {
  240. return nil, err
  241. }
  242. return resp, nil
  243. }
  244. // Add tags to the named ELB
  245. //
  246. // Note that AWS only accepts one ELB name at a time (even though it is sent as a list)
  247. //
  248. // See http://goo.gl/6JW4Wf for the rest of the details
  249. func (elb *ELB) AddTags(elbName string, tags map[string]string) (*SimpleResp, error) {
  250. var sortedKeys []string
  251. params := make(map[string]string)
  252. response := &SimpleResp{}
  253. for tagKey := range tags {
  254. sortedKeys = append(sortedKeys, tagKey)
  255. }
  256. sort.Strings(sortedKeys)
  257. for _, key := range sortedKeys {
  258. number := len(tags)
  259. params[fmt.Sprintf("Tags.member.%d.Key", number)] = key
  260. params[fmt.Sprintf("Tags.member.%d.Value", number)] = tags[key]
  261. delete(tags, key)
  262. }
  263. params["Action"] = "AddTags"
  264. params["LoadBalancerNames.member.1"] = elbName
  265. if err := elb.query(params, response); err != nil {
  266. return nil, err
  267. }
  268. return response, nil
  269. }
  270. // Remove tags from the named ELB
  271. //
  272. // Note that AWS only accepts one ELB name at a time (even though it is sent as a list)
  273. //
  274. // see http://goo.gl/ochFqo for more details
  275. func (elb *ELB) RemoveTags(elbName string, tagKeys []string) (*SimpleResp, error) {
  276. response := &SimpleResp{}
  277. params := make(map[string]string)
  278. params["Action"] = "RemoveTags"
  279. params["LoadBalancerNames.member.1"] = elbName
  280. for i, tagKey := range tagKeys {
  281. params[fmt.Sprintf("Tags.member.%d.Key", i+1)] = tagKey
  282. }
  283. if err := elb.query(params, response); err != nil {
  284. return nil, err
  285. }
  286. return response, nil
  287. }
  288. func (elb *ELB) query(params map[string]string, resp interface{}) error {
  289. params["Version"] = "2012-06-01"
  290. params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339)
  291. data := strings.NewReader(multimap(params).Encode())
  292. hreq, err := http.NewRequest("GET", elb.Region.ELBEndpoint+"/", data)
  293. if err != nil {
  294. return err
  295. }
  296. hreq.URL.RawQuery = multimap(params).Encode()
  297. token := elb.Auth.Token()
  298. if token != "" {
  299. hreq.Header.Set("X-Amz-Security-Token", token)
  300. }
  301. signer := aws.NewV4Signer(elb.Auth, "elasticloadbalancing", elb.Region)
  302. signer.Sign(hreq)
  303. r, err := http.DefaultClient.Do(hreq)
  304. if err != nil {
  305. return err
  306. }
  307. defer r.Body.Close()
  308. if r.StatusCode != 200 {
  309. return buildError(r)
  310. }
  311. return xml.NewDecoder(r.Body).Decode(resp)
  312. }
  313. // Error encapsulates an error returned by ELB.
  314. type Error struct {
  315. // HTTP status code
  316. StatusCode int
  317. // AWS error code
  318. Code string
  319. // The human-oriented error message
  320. Message string
  321. }
  322. func (err *Error) Error() string {
  323. if err.Code == "" {
  324. return err.Message
  325. }
  326. return fmt.Sprintf("%s (%s)", err.Message, err.Code)
  327. }
  328. type xmlErrors struct {
  329. Errors []Error `xml:"Error"`
  330. }
  331. func buildError(r *http.Response) error {
  332. var (
  333. err Error
  334. errors xmlErrors
  335. )
  336. xml.NewDecoder(r.Body).Decode(&errors)
  337. if len(errors.Errors) > 0 {
  338. err = errors.Errors[0]
  339. }
  340. err.StatusCode = r.StatusCode
  341. if err.Message == "" {
  342. err.Message = r.Status
  343. }
  344. return &err
  345. }
  346. func multimap(p map[string]string) url.Values {
  347. q := make(url.Values, len(p))
  348. for k, v := range p {
  349. q[k] = []string{v}
  350. }
  351. return q
  352. }
  353. func makeCreateParams(createLB *CreateLoadBalancer) map[string]string {
  354. params := make(map[string]string)
  355. params["LoadBalancerName"] = createLB.Name
  356. params["Action"] = "CreateLoadBalancer"
  357. if createLB.Scheme != "" {
  358. params["Scheme"] = createLB.Scheme
  359. }
  360. for i, s := range createLB.SecurityGroups {
  361. key := fmt.Sprintf("SecurityGroups.member.%d", i+1)
  362. params[key] = s
  363. }
  364. for i, s := range createLB.Subnets {
  365. key := fmt.Sprintf("Subnets.member.%d", i+1)
  366. params[key] = s
  367. }
  368. for i, l := range createLB.Listeners {
  369. key := "Listeners.member.%d.%s"
  370. index := i + 1
  371. params[fmt.Sprintf(key, index, "InstancePort")] = strconv.Itoa(l.InstancePort)
  372. params[fmt.Sprintf(key, index, "InstanceProtocol")] = l.InstanceProtocol
  373. params[fmt.Sprintf(key, index, "Protocol")] = l.Protocol
  374. params[fmt.Sprintf(key, index, "LoadBalancerPort")] = strconv.Itoa(l.LoadBalancerPort)
  375. }
  376. for i, az := range createLB.AvailabilityZones {
  377. key := fmt.Sprintf("AvailabilityZones.member.%d", i+1)
  378. params[key] = az
  379. }
  380. return params
  381. }