logstash-input-vespa 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65e09408c1ab044377bbcda59b2a201e1b05f7e599d69277b292922e2129449f
4
- data.tar.gz: 5aa868b54f814c5425dcd5c009ddd99753d0e00a6c214558b877a05cfe643bc0
3
+ metadata.gz: 5685d4ff93454cd5bb00019d87ac57e65ec96f80a27aba4e2a2a417b631250f0
4
+ data.tar.gz: 3456a806bc19ee7b64c64521719562e944aaa2eb2ea2c7c1524848f9659eabc7
5
5
  SHA512:
6
- metadata.gz: 9d91224c4efd9b85c5a3ea9fd1d0256072c77d83e4f7013ac643e0e140024e880e4006535c1063c083978494be5b904218681a89515b028ed1cb357c0b3d9d99
7
- data.tar.gz: 3c0b794ddc12bb622a49a393e97c4209007e7f568c953f09185f072f99b2ef09fca22d49f30ce2ca0d577335e7265fc5d649d52e5dbca7ebccefd76e2b17d704
6
+ metadata.gz: 8643c2ea6899c96047806b23d710278eb50fe76dca5b6a18cd428a4e2474307b99fe17383a8718efe6b44c5ccd535e57f01f87e600508a3316feafdfca8d5d98
7
+ data.tar.gz: 91e734d688e73b662cab9eea66c685ade66837007f0245d4a498ca1727d13ce95d3f06464634f6cf2da1b6a8e0b86e9cac8ca7a4a3e02312fd7b2b510f05a486
data/CHANGELOG.md CHANGED
@@ -1,14 +1,4 @@
1
- ## 2.0.7
2
- - Docs: Set the default_codec doc attribute.
1
+ ## 0.1.0
3
2
 
4
- ## 2.0.6
5
- - Docs: Add documentation template
6
- ## 2.0.4
7
- - Add encoding: utf-8 line to spec file. This can prevent issues with tests.
8
- ## 2.0.1
9
- - Simplify the shutdown implementation a bit for easier understanding
10
- ## 2.0.0
11
- - Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
12
- instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
13
- - Dependency on logstash-core update to 2.0
3
+ Initial version. Can connect to an HTTP endpoint of Vespa and pull documents via Visit and use the continuation token
14
4
 
data/README.md CHANGED
@@ -1,98 +1,26 @@
1
- # Logstash Plugin
1
+ # Logstash Input Plugin for Vespa
2
2
 
3
- [![Travis Build Status](https://travis-ci.com/logstash-plugins/logstash-input-example.svg)](https://travis-ci.com/logstash-plugins/logstash-input-example)
3
+ Plugin for [Logstash](https://github.com/elastic/logstash) to read from [Vespa](https://vespa.ai). Apache 2.0 license.
4
4
 
5
- This is a plugin for [Logstash](https://github.com/elastic/logstash).
6
-
7
- It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
8
-
9
- ## Documentation
10
-
11
- Logstash provides infrastructure to automatically build documentation for this plugin. We provide a template file, index.asciidoc, where you can add documentation. The contents of this file will be converted into html and then placed with other plugin documentation in a [central location](http://www.elastic.co/guide/en/logstash/current/).
12
-
13
- - For formatting config examples, you can use the asciidoc `[source,json]` directive
14
- - For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
15
-
16
- ## Need Help?
17
-
18
- Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
19
-
20
- ## Developing
21
-
22
- ### 1. Plugin Developement and Testing
23
-
24
- #### Code
25
- - To get started, you'll need JRuby with the Bundler gem installed.
26
-
27
- - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
28
-
29
- - Install dependencies
30
- ```sh
31
- bundle install
32
- ```
33
-
34
- #### Test
35
-
36
- - Update your dependencies
37
-
38
- ```sh
39
- bundle install
40
- ```
41
-
42
- - Run tests
43
-
44
- ```sh
45
- bundle exec rspec
46
- ```
47
-
48
- ### 2. Running your unpublished Plugin in Logstash
49
-
50
- #### 2.1 Run in a local Logstash clone
51
-
52
- - Edit Logstash `Gemfile` and add the local plugin path, for example:
53
- ```ruby
54
- gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
55
- ```
56
- - Install plugin
57
- ```sh
58
- # Logstash 2.3 and higher
59
- bin/logstash-plugin install --no-verify
60
-
61
- # Prior to Logstash 2.3
62
- bin/plugin install --no-verify
5
+ ## Installation
63
6
 
7
+ Download and unpack/install Logstash, then:
64
8
  ```
65
- - Run Logstash with your plugin
66
- ```sh
67
- bin/logstash -e 'filter {awesome {}}'
9
+ bin/logstash-plugin install logstash-input-vespa
68
10
  ```
69
- At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
70
11
 
71
- #### 2.2 Run in an installed Logstash
72
-
73
- You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
74
-
75
- - Build your plugin gem
76
- ```sh
77
- gem build logstash-filter-awesome.gemspec
78
- ```
79
- - Install the plugin from the Logstash home
80
- ```sh
81
- # Logstash 2.3 and higher
82
- bin/logstash-plugin install --no-verify
83
-
84
- # Prior to Logstash 2.3
85
- bin/plugin install --no-verify
12
+ ## Usage
86
13
 
14
+ Minimal Logstash config example:
87
15
  ```
88
- - Start Logstash and proceed to test the plugin
89
-
90
- ## Contributing
91
-
92
- All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
93
-
94
- Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
95
-
96
- It is more important to the community that you are able to contribute.
16
+ input {
17
+ vespa {
18
+ vespa_url => "http://localhost:8080"
19
+ cluster => "test_cluster"
20
+ }
21
+ }
97
22
 
98
- For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
23
+ output {
24
+ stdout {}
25
+ }
26
+ ```
@@ -4,6 +4,7 @@ require "logstash/namespace"
4
4
  require "net/http"
5
5
  require "uri"
6
6
  require "json"
7
+ require "openssl"
7
8
 
8
9
  # This is the logstash vespa input plugin. It is used to read from Vespa
9
10
  # via Visit : https://docs.vespa.ai/en/reference/document-v1-api-reference.html#visit
@@ -21,28 +22,71 @@ class LogStash::Inputs::Vespa < LogStash::Inputs::Base
21
22
  # The cluster parameter to use in the request.
22
23
  config :cluster, :validate => :string, :required => true
23
24
 
25
+ # Path to the client certificate file for mTLS.
26
+ config :client_cert, :validate => :path
27
+
28
+ # Path to the client key file for mTLS.
29
+ config :client_key, :validate => :path
30
+
31
+ # desired page size for the visit request, i.e. the wantedDocumentCount parameter
32
+ config :page_size, :validate => :number, :default => 100
33
+
34
+ # backend concurrency for the visit request, i.e. the concurrency parameter
35
+ config :backend_concurrency, :validate => :number, :default => 1
36
+
37
+ # selection. A query in Vespa selector language
38
+ config :selection, :validate => :string
39
+
40
+ # timeout for each HTTP request
41
+ config :timeout, :validate => :number, :default => 180
42
+
43
+ # lower timestamp limit for the visit request, i.e. the fromTimestamp parameter
44
+ # microseconds since epoch
45
+ config :from_timestamp, :validate => :number
46
+
47
+ # upper timestamp limit for the visit request, i.e. the toTimestamp parameter
48
+ config :to_timestamp, :validate => :number
49
+
24
50
  public
25
51
  def register
26
- # nothing to do here
52
+ if @client_cert != nil
53
+ @cert = OpenSSL::X509::Certificate.new(File.read(@client_cert))
54
+ end
55
+ if @client_key != nil
56
+ @key = OpenSSL::PKey::RSA.new(File.read(@client_key))
57
+ end
58
+
59
+ if @client_cert.nil? ^ @client_key.nil?
60
+ raise LogStash::ConfigurationError, "Both client_cert and client_key must be set, you can't have just one"
61
+ end
62
+
63
+ @uri_params = {
64
+ :cluster => @cluster,
65
+ :wantedDocumentCount => @page_size,
66
+ :concurrency => @backend_concurrency,
67
+ :timeout => @timeout
68
+ }
69
+
70
+ if @selection != nil
71
+ @uri_params[:selection] = @selection
72
+ end
73
+
74
+ if @from_timestamp != nil
75
+ @uri_params[:fromTimestamp] = @from_timestamp
76
+ end
77
+
78
+ if @to_timestamp != nil
79
+ @uri_params[:toTimestamp] = @to_timestamp
80
+ end
27
81
  end # def register
28
82
 
29
83
  def run(queue)
30
- uri = URI.parse("#{@vespa_url}/document/v1/?cluster=#{@cluster}")
84
+ uri = URI.parse("#{@vespa_url}/document/v1/")
85
+ uri.query = URI.encode_www_form(@uri_params)
31
86
  continuation = nil
32
87
 
33
88
  loop do
34
-
35
- if continuation != nil
36
- uri.query = URI.encode_www_form({:cluster => @cluster, :continuation => continuation})
37
- end
38
-
39
- # TODO we'll want to retry here
40
- begin
41
- response = Net::HTTP.get_response(uri)
42
- rescue => e
43
- @logger.error("Failed to make HTTP request to Vespa", :error => e.message)
44
- break
45
- end
89
+ response = fetch_documents_from_vespa(uri)
46
90
  # response should look like:
47
91
  # {
48
92
  # "pathId":"/document/v1/","documents":[
@@ -52,29 +96,31 @@ class LogStash::Inputs::Vespa < LogStash::Inputs::Base
52
96
  # }
53
97
 
54
98
  if response.is_a?(Net::HTTPSuccess)
55
- begin
56
- response_parsed = JSON.parse(response.body)
57
- rescue JSON::ParserError => e
58
- @logger.error("Failed to parse JSON response", :error => e.message)
59
- break
60
- end
99
+ response_parsed = parse_response(response)
100
+ break unless response_parsed
61
101
 
62
102
  document_count = response_parsed["documentCount"]
63
103
  # record the continuation token for the next request (if it exists)
64
104
  continuation = response_parsed["continuation"]
65
105
  documents = response_parsed["documents"]
66
106
 
67
- documents.each do |document|
68
- event = LogStash::Event.new(document["fields"])
69
- decorate(event)
70
- queue << event
71
- end # documents.each
107
+ process_documents(documents, queue)
72
108
 
73
109
  # Exit the loop if there are no more documents to process
74
- break if document_count == 0
110
+ if continuation != nil
111
+ uri.query = URI.encode_www_form(@uri_params.merge({:continuation => continuation}))
112
+ else
113
+ @logger.info("No continuation ID => no more documents to fetch from Vespa")
114
+ break
115
+ end
116
+
117
+ if @stopping
118
+ @logger.info("Stopping Vespa input")
119
+ break
120
+ end
75
121
 
76
122
  else
77
- @logger.error("Failed to fetch documents from Vespa",
123
+ @logger.error("Failed to fetch documents from Vespa", :request => uri.to_s,
78
124
  :response_code => response.code, :response_message => response.message)
79
125
  break # TODO retry? Only on certain codes?
80
126
  end # if response.is_a?(Net::HTTPSuccess)
@@ -82,11 +128,38 @@ class LogStash::Inputs::Vespa < LogStash::Inputs::Base
82
128
  end # loop do
83
129
  end # def run
84
130
 
131
+ def fetch_documents_from_vespa(uri)
132
+ http = Net::HTTP.new(uri.host, uri.port)
133
+ if uri.scheme == "https"
134
+ http.use_ssl = true
135
+ http.cert = @cert
136
+ http.key = @key
137
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
138
+ end
139
+
140
+ request = Net::HTTP::Get.new(uri.request_uri)
141
+ http.request(request)
142
+ rescue => e
143
+ @logger.error("Failed to make HTTP request to Vespa", :error => e.message)
144
+ nil
145
+ end # def fetch_documents_from_vespa
146
+
147
+ def parse_response(response)
148
+ JSON.parse(response.body)
149
+ rescue JSON::ParserError => e
150
+ @logger.error("Failed to parse JSON response", :error => e.message)
151
+ nil
152
+ end # def parse_response
153
+
154
+ def process_documents(documents, queue)
155
+ documents.each do |document|
156
+ event = LogStash::Event.new(document)
157
+ decorate(event)
158
+ queue << event
159
+ end
160
+ end # def process_documents
161
+
85
162
  def stop
86
- # TODO
87
- # examples of common "stop" tasks:
88
- # * close sockets (unblocking blocking reads/accepts)
89
- # * cleanup temporary files
90
- # * terminate spawned threads
163
+ @stopping = true
91
164
  end
92
165
  end # class LogStash::Inputs::Vespa
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-vespa'
3
- s.version = '0.1.0'
3
+ s.version = '0.2.0'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "Logstash input plugin reading from Vespa"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-vespa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radu Gheorghe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-28 00:00:00.000000000 Z
11
+ date: 2024-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -86,7 +86,6 @@ files:
86
86
  - README.md
87
87
  - lib/logstash/inputs/vespa.rb
88
88
  - logstash-input-vespa.gemspec
89
- - spec/inputs/vespa_spec.rb
90
89
  homepage: https://vespa.ai
91
90
  licenses:
92
91
  - Apache-2.0
@@ -112,5 +111,4 @@ rubygems_version: 3.3.26
112
111
  signing_key:
113
112
  specification_version: 4
114
113
  summary: Logstash input plugin reading from Vespa
115
- test_files:
116
- - spec/inputs/vespa_spec.rb
114
+ test_files: []
@@ -1,12 +0,0 @@
1
- # encoding: utf-8
2
- require "logstash/devutils/rspec/spec_helper"
3
- require "logstash/devutils/rspec/shared_examples"
4
- require "logstash/inputs/vespa"
5
-
6
- describe LogStash::Inputs::Vespa do
7
-
8
- it_behaves_like "an interruptible input plugin" do
9
- let(:config) { { "interval" => 100 } }
10
- end
11
-
12
- end