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.

151 lines
3.7 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. "encoding/hex"
  19. "errors"
  20. "io/ioutil"
  21. "github.com/Sirupsen/logrus"
  22. "github.com/minio/sha256-simd"
  23. "gopkg.in/olivere/elastic.v3"
  24. )
  25. // elasticQueue is a elasticsearch event notification queue.
  26. type elasticSearchNotify struct {
  27. Enable bool `json:"enable"`
  28. URL string `json:"url"`
  29. Index string `json:"index"`
  30. }
  31. func (e *elasticSearchNotify) Validate() error {
  32. if !e.Enable {
  33. return nil
  34. }
  35. if _, err := checkURL(e.URL); err != nil {
  36. return err
  37. }
  38. return nil
  39. }
  40. type elasticClient struct {
  41. *elastic.Client
  42. params elasticSearchNotify
  43. }
  44. // Connects to elastic search instance at URL.
  45. func dialElastic(esNotify elasticSearchNotify) (*elastic.Client, error) {
  46. if !esNotify.Enable {
  47. return nil, errNotifyNotEnabled
  48. }
  49. client, err := elastic.NewClient(
  50. elastic.SetURL(esNotify.URL),
  51. elastic.SetSniff(false),
  52. elastic.SetMaxRetries(10),
  53. )
  54. if err != nil {
  55. return nil, err
  56. }
  57. return client, nil
  58. }
  59. func newElasticNotify(accountID string) (*logrus.Logger, error) {
  60. esNotify := serverConfig.Notify.GetElasticSearchByID(accountID)
  61. // Dial to elastic search.
  62. client, err := dialElastic(esNotify)
  63. if err != nil {
  64. return nil, err
  65. }
  66. // Use the IndexExists service to check if a specified index exists.
  67. exists, err := client.IndexExists(esNotify.Index).Do()
  68. if err != nil {
  69. return nil, err
  70. }
  71. // Index does not exist, attempt to create it.
  72. if !exists {
  73. var createIndex *elastic.IndicesCreateResult
  74. createIndex, err = client.CreateIndex(esNotify.Index).Do()
  75. if err != nil {
  76. return nil, err
  77. }
  78. if !createIndex.Acknowledged {
  79. return nil, errors.New("Index not created")
  80. }
  81. }
  82. elasticCl := elasticClient{
  83. Client: client,
  84. params: esNotify,
  85. }
  86. elasticSearchLog := logrus.New()
  87. // Disable writing to console.
  88. elasticSearchLog.Out = ioutil.Discard
  89. // Add a elasticSearch hook.
  90. elasticSearchLog.Hooks.Add(elasticCl)
  91. // Set default JSON formatter.
  92. elasticSearchLog.Formatter = new(logrus.JSONFormatter)
  93. // Success, elastic search successfully initialized.
  94. return elasticSearchLog, nil
  95. }
  96. // Fire is required to implement logrus hook
  97. func (q elasticClient) Fire(entry *logrus.Entry) error {
  98. // Reflect on eventType and Key on their native type.
  99. entryStr, ok := entry.Data["EventType"].(string)
  100. if !ok {
  101. return nil
  102. }
  103. keyStr, ok := entry.Data["Key"].(string)
  104. if !ok {
  105. return nil
  106. }
  107. // Calculate a unique key id. Choosing sha256 here.
  108. shaKey := sha256.Sum256([]byte(keyStr))
  109. keyStr = hex.EncodeToString(shaKey[:])
  110. // If event matches as delete, we purge the previous index.
  111. if eventMatch(entryStr, []string{"s3:ObjectRemoved:*"}) {
  112. _, err := q.Client.Delete().Index(q.params.Index).
  113. Type("event").Id(keyStr).Do()
  114. if err != nil {
  115. return err
  116. }
  117. return nil
  118. } // else we update elastic index or create a new one.
  119. _, err := q.Client.Index().Index(q.params.Index).
  120. Type("event").
  121. BodyJson(map[string]interface{}{
  122. "Records": entry.Data["Records"],
  123. }).Id(keyStr).Do()
  124. return err
  125. }
  126. // Required for logrus hook implementation
  127. func (q elasticClient) Levels() []logrus.Level {
  128. return []logrus.Level{
  129. logrus.InfoLevel,
  130. }
  131. }