You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
3.6 KiB

  1. /*
  2. * Minio Cloud Storage, (C) 2016 Minio, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package cmd
  17. import (
  18. "io/ioutil"
  19. "net"
  20. "github.com/Sirupsen/logrus"
  21. "github.com/streadway/amqp"
  22. )
  23. // amqpNotify - represents logrus compatible AMQP hook.
  24. // All fields represent AMQP configuration details.
  25. type amqpNotify struct {
  26. Enable bool `json:"enable"`
  27. URL string `json:"url"`
  28. Exchange string `json:"exchange"`
  29. RoutingKey string `json:"routingKey"`
  30. ExchangeType string `json:"exchangeType"`
  31. Mandatory bool `json:"mandatory"`
  32. Immediate bool `json:"immediate"`
  33. Durable bool `json:"durable"`
  34. Internal bool `json:"internal"`
  35. NoWait bool `json:"noWait"`
  36. AutoDeleted bool `json:"autoDeleted"`
  37. }
  38. func (a *amqpNotify) Validate() error {
  39. if !a.Enable {
  40. return nil
  41. }
  42. if _, err := checkURL(a.URL); err != nil {
  43. return err
  44. }
  45. return nil
  46. }
  47. type amqpConn struct {
  48. params amqpNotify
  49. *amqp.Connection
  50. }
  51. // dialAMQP - dials and returns an amqpConnection instance,
  52. // for sending notifications. Returns error if amqp logger
  53. // is not enabled.
  54. func dialAMQP(amqpL amqpNotify) (amqpConn, error) {
  55. if !amqpL.Enable {
  56. return amqpConn{}, errNotifyNotEnabled
  57. }
  58. conn, err := amqp.Dial(amqpL.URL)
  59. if err != nil {
  60. return amqpConn{}, err
  61. }
  62. return amqpConn{Connection: conn, params: amqpL}, nil
  63. }
  64. func newAMQPNotify(accountID string) (*logrus.Logger, error) {
  65. amqpL := serverConfig.Notify.GetAMQPByID(accountID)
  66. // Connect to amqp server.
  67. amqpC, err := dialAMQP(amqpL)
  68. if err != nil {
  69. return nil, err
  70. }
  71. amqpLog := logrus.New()
  72. // Disable writing to console.
  73. amqpLog.Out = ioutil.Discard
  74. // Add a amqp hook.
  75. amqpLog.Hooks.Add(amqpC)
  76. // Set default JSON formatter.
  77. amqpLog.Formatter = new(logrus.JSONFormatter)
  78. // Successfully enabled all AMQPs.
  79. return amqpLog, nil
  80. }
  81. // Fire is called when an event should be sent to the message broker.
  82. func (q amqpConn) Fire(entry *logrus.Entry) error {
  83. ch, err := q.Connection.Channel()
  84. if err != nil {
  85. // Any other error other than connection closed, return.
  86. isClosedErr := false
  87. if neterr, ok := err.(*net.OpError); ok &&
  88. neterr.Err.Error() == "use of closed network connection" {
  89. isClosedErr = true
  90. } else if err == amqp.ErrClosed {
  91. isClosedErr = true
  92. }
  93. if !isClosedErr {
  94. return err
  95. }
  96. // Attempt to connect again.
  97. var conn *amqp.Connection
  98. conn, err = amqp.Dial(q.params.URL)
  99. if err != nil {
  100. return err
  101. }
  102. ch, err = conn.Channel()
  103. if err != nil {
  104. return err
  105. }
  106. }
  107. defer ch.Close()
  108. err = ch.ExchangeDeclare(
  109. q.params.Exchange,
  110. q.params.ExchangeType,
  111. q.params.Durable,
  112. q.params.AutoDeleted,
  113. q.params.Internal,
  114. q.params.NoWait,
  115. nil,
  116. )
  117. if err != nil {
  118. return err
  119. }
  120. body, err := entry.String()
  121. if err != nil {
  122. return err
  123. }
  124. err = ch.Publish(
  125. q.params.Exchange,
  126. q.params.RoutingKey,
  127. q.params.Mandatory,
  128. q.params.Immediate,
  129. amqp.Publishing{
  130. ContentType: "application/json",
  131. Body: []byte(body),
  132. })
  133. if err != nil {
  134. return err
  135. }
  136. return nil
  137. }
  138. // Levels is available logging levels.
  139. func (q amqpConn) Levels() []logrus.Level {
  140. return []logrus.Level{
  141. logrus.InfoLevel,
  142. }
  143. }