connectors_service 8.5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +93 -0
  3. data/NOTICE.txt +2 -0
  4. data/bin/connectors_service +4 -0
  5. data/bin/list_connectors +4 -0
  6. data/config/connectors.yml +25 -0
  7. data/lib/app/app.rb +25 -0
  8. data/lib/app/config.rb +132 -0
  9. data/lib/app/console_app.rb +278 -0
  10. data/lib/app/dispatcher.rb +121 -0
  11. data/lib/app/menu.rb +104 -0
  12. data/lib/app/preflight_check.rb +134 -0
  13. data/lib/app/version.rb +10 -0
  14. data/lib/connectors/base/adapter.rb +119 -0
  15. data/lib/connectors/base/connector.rb +57 -0
  16. data/lib/connectors/base/custom_client.rb +111 -0
  17. data/lib/connectors/connector_status.rb +31 -0
  18. data/lib/connectors/crawler/scheduler.rb +32 -0
  19. data/lib/connectors/example/connector.rb +57 -0
  20. data/lib/connectors/example/example_attachments/first_attachment.txt +1 -0
  21. data/lib/connectors/example/example_attachments/second_attachment.txt +1 -0
  22. data/lib/connectors/example/example_attachments/third_attachment.txt +1 -0
  23. data/lib/connectors/gitlab/adapter.rb +50 -0
  24. data/lib/connectors/gitlab/connector.rb +67 -0
  25. data/lib/connectors/gitlab/custom_client.rb +44 -0
  26. data/lib/connectors/gitlab/extractor.rb +69 -0
  27. data/lib/connectors/mongodb/connector.rb +138 -0
  28. data/lib/connectors/registry.rb +52 -0
  29. data/lib/connectors/sync_status.rb +21 -0
  30. data/lib/connectors.rb +16 -0
  31. data/lib/connectors_app/// +13 -0
  32. data/lib/connectors_service.rb +24 -0
  33. data/lib/connectors_utility.rb +16 -0
  34. data/lib/core/configuration.rb +48 -0
  35. data/lib/core/connector_settings.rb +142 -0
  36. data/lib/core/elastic_connector_actions.rb +269 -0
  37. data/lib/core/heartbeat.rb +32 -0
  38. data/lib/core/native_scheduler.rb +24 -0
  39. data/lib/core/output_sink/base_sink.rb +33 -0
  40. data/lib/core/output_sink/combined_sink.rb +38 -0
  41. data/lib/core/output_sink/console_sink.rb +51 -0
  42. data/lib/core/output_sink/es_sink.rb +74 -0
  43. data/lib/core/output_sink.rb +13 -0
  44. data/lib/core/scheduler.rb +158 -0
  45. data/lib/core/single_scheduler.rb +29 -0
  46. data/lib/core/sync_job_runner.rb +111 -0
  47. data/lib/core.rb +16 -0
  48. data/lib/list_connectors.rb +22 -0
  49. data/lib/stubs/app_config.rb +35 -0
  50. data/lib/stubs/connectors/stats.rb +35 -0
  51. data/lib/stubs/service_type.rb +13 -0
  52. data/lib/utility/constants.rb +20 -0
  53. data/lib/utility/cron.rb +81 -0
  54. data/lib/utility/elasticsearch/index/language_data.yml +111 -0
  55. data/lib/utility/elasticsearch/index/mappings.rb +104 -0
  56. data/lib/utility/elasticsearch/index/text_analysis_settings.rb +226 -0
  57. data/lib/utility/environment.rb +33 -0
  58. data/lib/utility/errors.rb +132 -0
  59. data/lib/utility/es_client.rb +84 -0
  60. data/lib/utility/exception_tracking.rb +64 -0
  61. data/lib/utility/extension_mapping_util.rb +123 -0
  62. data/lib/utility/logger.rb +84 -0
  63. data/lib/utility/middleware/basic_auth.rb +27 -0
  64. data/lib/utility/middleware/bearer_auth.rb +27 -0
  65. data/lib/utility/middleware/restrict_hostnames.rb +73 -0
  66. data/lib/utility.rb +16 -0
  67. metadata +487 -0
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'core/scheduler'
10
+ require 'core/connector_settings'
11
+ require 'core/elastic_connector_actions'
12
+ require 'utility/logger'
13
+ require 'utility/exception_tracking'
14
+
15
+ module Connectors
16
+ module Crawler
17
+ class Scheduler < Core::Scheduler
18
+ def connector_settings
19
+ Core::ConnectorSettings.fetch_crawler_connectors || []
20
+ rescue StandardError => e
21
+ Utility::ExceptionTracking.log_exception(e, 'Could not retrieve Crawler connectors due to unexpected error.')
22
+ []
23
+ end
24
+
25
+ private
26
+
27
+ def connector_registered?(service_type)
28
+ service_type == 'elastic-crawler'
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,57 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'connectors/base/connector'
10
+ require 'utility'
11
+
12
+ module Connectors
13
+ module Example
14
+ class Connector < Connectors::Base::Connector
15
+ def self.service_type
16
+ 'example'
17
+ end
18
+
19
+ def self.display_name
20
+ 'Example Connector'
21
+ end
22
+
23
+ def self.configurable_fields
24
+ {
25
+ 'foo' => {
26
+ 'label' => 'Foo',
27
+ 'value' => nil
28
+ }
29
+ }
30
+ end
31
+
32
+ def initialize(configuration: {})
33
+ super
34
+ end
35
+
36
+ def do_health_check
37
+ # Do the health check by trying to access 3rd-party system just to verify that everything is set up properly.
38
+ #
39
+ # To emulate unhealthy 3rd-party system situation, uncomment the following line:
40
+ # raise 'something went wrong'
41
+ end
42
+
43
+ def yield_documents
44
+ attachments = [
45
+ File.open('./lib/connectors/example/example_attachments/first_attachment.txt'),
46
+ File.open('./lib/connectors/example/example_attachments/second_attachment.txt'),
47
+ File.open('./lib/connectors/example/example_attachments/third_attachment.txt')
48
+ ]
49
+
50
+ attachments.each_with_index do |att, index|
51
+ data = { id: (index + 1).to_s, name: "example document #{index + 1}", _attachment: File.read(att) }
52
+ yield data
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1 @@
1
+ e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM5Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRmb3JtMHtcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYS1Cb2xkO1xmMVxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTtcZjJcZnN3aXNzXGZjaGFyc2V0MCBIZWx2ZXRpY2EtT2JsaXF1ZTsKfQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9CntcKlxleHBhbmRlZGNvbG9ydGJsOzt9ClxwYXBlcncxMTkwMFxwYXBlcmgxNjg0MFxtYXJnbDE0NDBcbWFyZ3IxNDQwXHZpZXd3MTE1MjBcdmlld2g4NDAwXHZpZXdraW5kMApccGFyZFx0eDU2Nlx0eDExMzNcdHgxNzAwXHR4MjI2N1x0eDI4MzRcdHgzNDAxXHR4Mzk2OFx0eDQ1MzVcdHg1MTAyXHR4NTY2OVx0eDYyMzZcdHg2ODAzXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMjQgXGNmMCBFeGFtcGxlIEF0dGFjaG1lbnQgMDFcClwKClxmMVxiMCBUaGlzIGlzIHRoZQpcZjJcaSAgZmlyc3QgClxmMVxpMCBvZiBcdWwgdGhyZWUgXHVsbm9uZSBleGFtcGxlIGF0dGFjaG1lbnRzfQ==
@@ -0,0 +1 @@
1
+ e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM5Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRmb3JtMHtcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYS1Cb2xkO1xmMVxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTtcZjJcZnN3aXNzXGZjaGFyc2V0MCBIZWx2ZXRpY2EtT2JsaXF1ZTsKfQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9CntcKlxleHBhbmRlZGNvbG9ydGJsOzt9ClxwYXBlcncxMTkwMFxwYXBlcmgxNjg0MFxtYXJnbDE0NDBcbWFyZ3IxNDQwXHZpZXd3MTE1MjBcdmlld2g4NDAwXHZpZXdraW5kMApccGFyZFx0eDU2Nlx0eDExMzNcdHgxNzAwXHR4MjI2N1x0eDI4MzRcdHgzNDAxXHR4Mzk2OFx0eDQ1MzVcdHg1MTAyXHR4NTY2OVx0eDYyMzZcdHg2ODAzXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMjQgXGNmMCBFeGFtcGxlIEF0dGFjaG1lbnQgMDJcClwKClxmMVxiMCBUaGlzIGlzIHRoZQpcZjJcaSAgc2Vjb25kIApcZjFcaTAgb2YgXHVsIHRocmVlIFx1bG5vbmUgZXhhbXBsZSBhdHRhY2htZW50c30=
@@ -0,0 +1 @@
1
+ e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM5Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRmb3JtMHtcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYS1Cb2xkO1xmMVxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTtcZjJcZnN3aXNzXGZjaGFyc2V0MCBIZWx2ZXRpY2EtT2JsaXF1ZTsKfQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9CntcKlxleHBhbmRlZGNvbG9ydGJsOzt9ClxwYXBlcncxMTkwMFxwYXBlcmgxNjg0MFxtYXJnbDE0NDBcbWFyZ3IxNDQwXHZpZXd3MTE1MjBcdmlld2g4NDAwXHZpZXdraW5kMApccGFyZFx0eDU2Nlx0eDExMzNcdHgxNzAwXHR4MjI2N1x0eDI4MzRcdHgzNDAxXHR4Mzk2OFx0eDQ1MzVcdHg1MTAyXHR4NTY2OVx0eDYyMzZcdHg2ODAzXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMjQgXGNmMCBFeGFtcGxlIEF0dGFjaG1lbnQgMDNcClwKClxmMVxiMCBUaGlzIGlzIHRoZQpcZjJcaSAgdGhpcmQgClxmMVxpMCBvZiBcdWwgdGhyZWUgXHVsbm9uZSBleGFtcGxlIGF0dGFjaG1lbnRzfQ==
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
5
+ # or more contributor license agreements. Licensed under the Elastic License;
6
+ # you may not use this file except in compliance with the Elastic License.
7
+ #
8
+ require 'hashie/mash'
9
+ require 'active_support/core_ext/hash/indifferent_access'
10
+ require 'connectors/base/adapter'
11
+
12
+ module Connectors
13
+ module GitLab
14
+ class Adapter < Connectors::Base::Adapter
15
+ # it's important to have this to generate ID converters between the GitLab ID and the
16
+ # Enterprise Search document ID. The Enterprise Search document ID will be prefixed with the service type,
17
+ # in our case - `gitlab`.
18
+ generate_id_helpers :gitlab, 'gitlab'
19
+
20
+ def self.to_es_document(type, source_doc)
21
+ source_doc = source_doc.with_indifferent_access
22
+ result = {}
23
+ case type.to_sym
24
+ when :project
25
+ result.merge!(
26
+ {
27
+ :url => source_doc[:web_url],
28
+ :body => source_doc[:description],
29
+ :title => source_doc[:name],
30
+ :created_at => source_doc[:created_at],
31
+ :last_modified_at => source_doc[:last_activity_at],
32
+ :visibility => source_doc[:visibility],
33
+ :namespace => if source_doc[:namespace].nil?
34
+ nil
35
+ else
36
+ source_doc[:namespace][:name]
37
+ end
38
+ }
39
+ )
40
+ else
41
+ # don't remap
42
+ result.merge!(source_doc)
43
+ end
44
+ result[:id] = gitlab_id_to_es_id(source_doc[:id])
45
+ result[:type] = type
46
+ result
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,67 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+ require 'active_support/core_ext/hash/indifferent_access'
9
+
10
+ require 'connectors/base/connector'
11
+ require 'connectors/gitlab/extractor'
12
+ require 'connectors/gitlab/custom_client'
13
+ require 'connectors/gitlab/adapter'
14
+ require 'core/output_sink'
15
+
16
+ module Connectors
17
+ module GitLab
18
+ class Connector < Connectors::Base::Connector
19
+ def self.service_type
20
+ 'gitlab'
21
+ end
22
+
23
+ def self.display_name
24
+ 'GitLab Connector'
25
+ end
26
+
27
+ def self.configurable_fields
28
+ {
29
+ :base_url => {
30
+ :label => 'Base URL',
31
+ :value => Connectors::GitLab::DEFAULT_BASE_URL
32
+ },
33
+ :api_key => {
34
+ :label => 'API Key'
35
+ }
36
+ }
37
+ end
38
+
39
+ def initialize(configuration: {})
40
+ super
41
+
42
+ @extractor = Connectors::GitLab::Extractor.new(
43
+ :base_url => configuration.dig(:base_url, :value),
44
+ :api_token => configuration.dig(:api_token, :value)
45
+ )
46
+ end
47
+
48
+ def yield_documents
49
+ next_page_link = nil
50
+ loop do
51
+ next_page_link = @extractor.yield_projects_page(next_page_link) do |projects_chunk|
52
+ projects_chunk.each do |project|
53
+ yield Connectors::GitLab::Adapter.to_es_document(:project, project)
54
+ end
55
+ end
56
+ break unless next_page_link.present?
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def do_health_check
63
+ @extractor.health_check
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+ require 'faraday_middleware/response/follow_redirects'
7
+ require 'connectors/base/custom_client'
8
+ require 'utility/middleware/bearer_auth'
9
+ require 'utility/middleware/basic_auth'
10
+ require 'utility/middleware/restrict_hostnames'
11
+
12
+ require 'app/config'
13
+
14
+ module Connectors
15
+ module GitLab
16
+ DEFAULT_BASE_URL = 'https://gitlab.com/api/v4'
17
+
18
+ class CustomClient < Connectors::Base::CustomClient
19
+ attr_reader :api_token
20
+
21
+ class ClientError < StandardError
22
+ attr_reader :status_code, :endpoint, :api_token
23
+
24
+ def initialize(status_code, endpoint)
25
+ @status_code = status_code
26
+ @endpoint = endpoint
27
+ end
28
+ end
29
+
30
+ def initialize(base_url:, api_token:, ensure_fresh_auth: nil)
31
+ @api_token = api_token
32
+ super(:base_url => base_url || DEFAULT_BASE_URL, :ensure_fresh_auth => ensure_fresh_auth)
33
+ end
34
+
35
+ def additional_middleware
36
+ [
37
+ ::FaradayMiddleware::FollowRedirects,
38
+ [Utility::Middleware::RestrictHostnames, { :allowed_hosts => [base_url, DEFAULT_BASE_URL] }],
39
+ [Utility::Middleware::BearerAuth, { :bearer_auth_token => api_token }]
40
+ ]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'hashie'
10
+ require 'json'
11
+ require 'rack/utils'
12
+ require 'active_support/core_ext/hash/indifferent_access'
13
+ require 'connectors/gitlab/custom_client'
14
+
15
+ module Connectors
16
+ module GitLab
17
+ class Extractor
18
+ PAGE_SIZE = 100 # max is 100
19
+
20
+ def initialize(base_url: nil, api_token: nil, owned_only: true)
21
+ super()
22
+ @base_url = base_url
23
+ @api_token = api_token
24
+ # only get projects that user owns
25
+ @owned_only = owned_only
26
+ end
27
+
28
+ def yield_projects_page(next_page_link = nil)
29
+ query_params = {
30
+ :pagination => :keyset,
31
+ :per_page => PAGE_SIZE,
32
+ :order_by => :id,
33
+ :sort => :desc,
34
+ :owned => @owned_only
35
+ }
36
+
37
+ if next_page_link.present?
38
+ if (matcher = /(https?:[^>]*)/.match(next_page_link))
39
+ clean_query = URI.parse(matcher.captures[0]).query
40
+ query_params = Rack::Utils.parse_query(clean_query)
41
+ else
42
+ raise "Next page link has unexpected format: #{next_page_link}"
43
+ end
44
+ end
45
+ response = client.get('projects', query_params)
46
+
47
+ projects_chunk = JSON.parse(response.body)
48
+ yield projects_chunk
49
+
50
+ # return next link
51
+ response.headers['Link'] || nil
52
+ end
53
+
54
+ def health_check
55
+ # let's do a simple call to get the current user
56
+ response = client.get('user')
57
+ unless response.present? && response.status == 200
58
+ raise "Health check failed with response status #{response.status} and body #{response.body}"
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def client
65
+ @client ||= Connectors::GitLab::CustomClient.new(base_url: @base_url, api_token: @api_token)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,138 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'active_support/core_ext/hash/indifferent_access'
10
+ require 'connectors/base/connector'
11
+ require 'mongo'
12
+
13
+ module Connectors
14
+ module MongoDB
15
+ class Connector < Connectors::Base::Connector
16
+ def self.service_type
17
+ 'mongodb'
18
+ end
19
+
20
+ def self.display_name
21
+ 'MongoDB'
22
+ end
23
+
24
+ def self.configurable_fields
25
+ {
26
+ :host => {
27
+ :label => 'Server Hostname'
28
+ },
29
+ :user => {
30
+ :label => 'Username'
31
+ },
32
+ :password => {
33
+ :label => 'Password'
34
+ },
35
+ :database => {
36
+ :label => 'Database'
37
+ },
38
+ :collection => {
39
+ :label => 'Collection'
40
+ },
41
+ :direct_connection => {
42
+ :label => 'Direct connection? (true/false)'
43
+ }
44
+ }
45
+ end
46
+
47
+ def initialize(configuration: {})
48
+ super
49
+
50
+ @host = configuration.dig(:host, :value)
51
+ @database = configuration.dig(:database, :value)
52
+ @collection = configuration.dig(:collection, :value)
53
+ @user = configuration.dig(:user, :value)
54
+ @password = configuration.dig(:password, :value)
55
+ @direct_connection = configuration.dig(:direct_connection, :value)
56
+ end
57
+
58
+ def yield_documents
59
+ with_client do |client|
60
+ client[@collection].find.each do |document|
61
+ doc = document.with_indifferent_access
62
+
63
+ yield serialize(doc)
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def do_health_check
71
+ with_client do |_client|
72
+ Utility::Logger.debug("Mongo at #{@host}/#{@database} looks healthy.")
73
+ end
74
+ end
75
+
76
+ def with_client
77
+ raise "Invalid value for 'Direct connection' : #{@direct_connection}." unless %w[true false].include?(@direct_connection.to_s.strip.downcase)
78
+
79
+ client = if @user.present? || @password.present?
80
+ Mongo::Client.new(
81
+ @host,
82
+ database: @database,
83
+ direct_connection: to_boolean(@direct_connection),
84
+ user: @user,
85
+ password: @password
86
+ )
87
+ else
88
+ Mongo::Client.new(
89
+ @host,
90
+ database: @database,
91
+ direct_connection: to_boolean(@direct_connection)
92
+ )
93
+ end
94
+
95
+ begin
96
+ Utility::Logger.debug("Existing Databases #{client.database_names}")
97
+ Utility::Logger.debug('Existing Collections:')
98
+
99
+ client.collections.each { |coll| Utility::Logger.debug(coll.name) }
100
+
101
+ yield client
102
+ ensure
103
+ client.close
104
+ end
105
+ end
106
+
107
+ def serialize(mongodb_document)
108
+ # This is some lazy serialization here.
109
+ # Problem: MongoDB has its own format of things - e.g. ids are Bson::ObjectId, which when serialized to JSON
110
+ # will produce something like: 'id': { '$oid': '536268a06d2d7019ba000000' }, which is not good for us
111
+ case mongodb_document
112
+ when BSON::ObjectId
113
+ mongodb_document.to_s
114
+ when BSON::Decimal128
115
+ mongodb_document.to_big_decimal # potential problems with NaNs but also will get treated as a string by Elasticsearch anyway
116
+ when String
117
+ # it's here cause Strings are Arrays too :/
118
+ mongodb_document.to_s
119
+ when Array
120
+ mongodb_document.map { |v| serialize(v) }
121
+ when Hash
122
+ mongodb_document.map do |key, value|
123
+ remapped_key = key == '_id' ? 'id' : key
124
+
125
+ remapped_value = serialize(value)
126
+ [remapped_key, remapped_value]
127
+ end.to_h
128
+ else
129
+ mongodb_document
130
+ end
131
+ end
132
+
133
+ def to_boolean(value)
134
+ value == true || value =~ (/(true|t|yes|y|1)$/i)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ module Connectors
8
+ class Factory
9
+ attr_reader :connectors
10
+
11
+ def initialize
12
+ @connectors = {}
13
+ end
14
+
15
+ def register(name, klass)
16
+ @connectors[name] = klass
17
+ end
18
+
19
+ def registered?(name)
20
+ @connectors.has_key?(name)
21
+ end
22
+
23
+ def connector_class(name)
24
+ @connectors[name]
25
+ end
26
+
27
+ def connector(name, configuration)
28
+ klass = connector_class(name)
29
+ if klass.present?
30
+ return klass.new(configuration: configuration)
31
+ end
32
+ raise "Connector #{name} is not yet registered. You need to register it before use"
33
+ end
34
+
35
+ def registered_connectors
36
+ @connectors.keys.sort
37
+ end
38
+ end
39
+
40
+ REGISTRY = Factory.new
41
+
42
+ require_relative './example/connector'
43
+ REGISTRY.register(Connectors::Example::Connector.service_type, Connectors::Example::Connector)
44
+
45
+ # loading plugins (might replace this with a directory scan and conventions on names)
46
+ require_relative './gitlab/connector'
47
+
48
+ REGISTRY.register(Connectors::GitLab::Connector.service_type, Connectors::GitLab::Connector)
49
+
50
+ require_relative 'mongodb/connector'
51
+ REGISTRY.register(Connectors::MongoDB::Connector.service_type, Connectors::MongoDB::Connector)
52
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ module Connectors
10
+ class SyncStatus
11
+ COMPLETED = 'completed'
12
+ IN_PROGRESS = 'in_progress'
13
+ FAILED = 'failed'
14
+
15
+ STATUSES = [
16
+ COMPLETED,
17
+ IN_PROGRESS,
18
+ FAILED
19
+ ]
20
+ end
21
+ end
data/lib/connectors.rb ADDED
@@ -0,0 +1,16 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ require 'utility'
8
+
9
+ def required_path(absolute_path)
10
+ absolute_dir = File.dirname(absolute_path)
11
+ relative_dir = absolute_dir.sub(/.*lib\/connectors/, 'connectors')
12
+ name = File.basename(absolute_path, '.rb')
13
+ File.join(relative_dir, name)
14
+ end
15
+
16
+ Dir[File.join(__dir__, 'connectors/**/*.rb')].each { |f| require required_path(f) }
@@ -0,0 +1,13 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ module ConnectorsApp
8
+ module Errors
9
+ INVALID_API_KEY = 'INVALID_API_KEY'
10
+ UNSUPPORTED_AUTH_SCHEME = 'UNSUPPORTED_AUTH_SCHEME'
11
+ INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR'
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'app/config'
10
+ require 'app/dispatcher'
11
+ require 'app/preflight_check'
12
+ require 'utility'
13
+
14
+ class ConnectorsService
15
+ def self.run!
16
+ Utility::Environment.set_execution_environment(App::Config) do
17
+ App::PreflightCheck.run!
18
+ App::Dispatcher.start!
19
+ rescue App::PreflightCheck::CheckFailure => e
20
+ Utility::Logger.error("Preflight check failed: #{e.message}")
21
+ exit(-1)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require_relative 'utility'
10
+
11
+ require_relative 'connectors/connector_status'
12
+ require_relative 'connectors/sync_status'
13
+ require_relative 'core/scheduler'
14
+ require_relative 'core/elastic_connector_actions'
15
+
16
+ require_relative 'connectors/crawler/scheduler'
@@ -0,0 +1,48 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'connectors/connector_status'
10
+ require 'connectors/registry'
11
+ require 'core/connector_settings'
12
+ require 'core/elastic_connector_actions'
13
+ require 'utility/logger'
14
+
15
+ module Core
16
+ class Configuration
17
+ class << self
18
+
19
+ def update(connector_settings, service_type = nil)
20
+ if connector_settings.connector_status == Connectors::ConnectorStatus::CREATED
21
+ connector_class = Connectors::REGISTRY.connector_class(connector_settings.service_type || service_type)
22
+ unless connector_class
23
+ Utility::Logger.error("Couldn't find connector for service type #{connector_settings.service_type || service_type}")
24
+ return
25
+ end
26
+ configuration = connector_class.configurable_fields
27
+ doc = {
28
+ :configuration => configuration
29
+ }
30
+
31
+ doc[:service_type] = service_type if service_type && connector_settings.needs_service_type?
32
+
33
+ # We want to set connector to CONFIGURED status if all configurable fields have default values
34
+ new_connector_status = if configuration.values.all? { |setting| setting[:value].present? }
35
+ Utility::Logger.debug("All connector configurable fields provided default values for #{connector_settings.formatted}.")
36
+ Connectors::ConnectorStatus::CONFIGURED
37
+ else
38
+ Connectors::ConnectorStatus::NEEDS_CONFIGURATION
39
+ end
40
+
41
+ doc[:status] = new_connector_status
42
+ Utility::Logger.info("Changing connector status to #{new_connector_status} for #{connector_settings.formatted}.")
43
+ Core::ElasticConnectorActions.update_connector_fields(connector_settings.id, doc)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end