logstash-input-oci 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0d6c2451fc751fafbdb326bf47a615e2a98fe47f384ccc819f3849829fec9476
4
+ data.tar.gz: dd01921b08ea39b39cb58430c8849720969d67d4051834eea83f2b6a6ad598aa
5
+ SHA512:
6
+ metadata.gz: 613b0946472b74351a27fb2207eed0ab2f293cf25ad13aa550449e56f050f79fbbff3ac7a2da0ab1c20bfea826e443ba4c33c61b930fe8a26e3eadc5a065bd3b
7
+ data.tar.gz: 0c80c285cd530f40d532e17b5c89db2cddf5a1dba4f98f9d8ee3766e212feff9f7ff233b5b046590db0779f7cb5037a01ca372fb9d8f6092392f052da0737b0f
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ ## 0.0.1
2
+ First Version
data/DEVELOPER.md ADDED
@@ -0,0 +1,2 @@
1
+ # logstash-input-example
2
+ Example input plugin. This should help bootstrap your effort to write your own input plugin!
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ logstash_path = ENV["LOGSTASH_PATH"] || "../../logstash"
6
+ use_logstash_source = ENV["LOGSTASH_SOURCE"] && ENV["LOGSTASH_SOURCE"].to_s == "1"
7
+
8
+
9
+ if Dir.exist?(logstash_path) && use_logstash_source
10
+ gem 'logstash-core', :path => "#{logstash_path}/logstash-core"
11
+ gem 'logstash-core-plugin-api', :path => "#{logstash_path}/logstash-core-plugin-api"
12
+ end
data/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2020 Elastic and contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
data/NOTICE.TXT ADDED
@@ -0,0 +1,5 @@
1
+ Elasticsearch
2
+ Copyright 2012-2015 Elasticsearch
3
+
4
+ This product includes software developed by The Apache Software
5
+ Foundation (http://www.apache.org/).
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # Logstash Plugin
2
+
3
+ [![Travis Build Status](https://travis-ci.com/logstash-plugins/logstash-input-oci_object_storage.svg)](https://travis-ci.com/logstash-plugins/logstash-input-oci_object_storage)
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 Development 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 an existing one 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
+ - Run tests
35
+ ```sh
36
+ bundle exec rspec
37
+ ```
38
+
39
+ ### 2. Running your unpublished Plugin in Logstash
40
+
41
+ #### 2.1 Run in a local Logstash clone
42
+ * Edit Logstash Gemfile and add the local plugin path, for example:
43
+ ```sh
44
+ gem "logstash-input-oci_object_storage", :path => "/your/local/logstash-input-oci_object_storage"
45
+ ```
46
+
47
+ * Install plugin
48
+ ```sh
49
+ # Logstash 2.3 and higher
50
+ bin/logstash-plugin install --no-verify
51
+
52
+ # Prior to Logstash 2.3
53
+ bin/plugin install --no-verify
54
+ ```
55
+
56
+ * Run Logstash with your plugin
57
+ ```sh
58
+ bin/logstash -e 'input { oci_object_storage { namespace => "your_namespace" bucket_name => "your_bucket_name" config_path => "your_config_path" } }'
59
+ ```
60
+
61
+ At this point, any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
62
+
63
+ #### 2.2 Run in an installed Logstash
64
+
65
+ 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:
66
+
67
+ * Build your plugin gem
68
+ ```sh
69
+ gem build logstash-input-oci_object_storage.gemspec
70
+ ```
71
+
72
+ * Install the plugin from the Logstash home
73
+ ```sh
74
+ # Logstash 2.3 and higher
75
+ bin/logstash-plugin install --no-verify
76
+
77
+ # Prior to Logstash 2.3
78
+ bin/plugin install --no-verify
79
+ ```
80
+
81
+ * Start Logstash and proceed to test the plugin
82
+
83
+ ## Contributing
84
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
85
+
86
+ 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.
87
+
88
+ It is more important to the community that you are able to contribute.
89
+
90
+ For more information about contributing, see the CONTRIBUTING file.
91
+
@@ -0,0 +1,326 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oci'
4
+ require 'zlib'
5
+ require 'date'
6
+ require 'tmpdir'
7
+ require 'stringio'
8
+ require 'thread/pool'
9
+ require 'stud/interval'
10
+ require 'logstash/namespace'
11
+ require 'logstash/inputs/base'
12
+
13
+ # The ObjectStorageGetter class is a utility class used by the OciObjectStorage Logstash input
14
+ # plugin to interact with Oracle Cloud Infrastructure (OCI) Object Storage.
15
+ # It provides methods to retrieve, process, and archive files from a specified OCI Object Storage bucket.
16
+ class ObjectStorageGetter
17
+ attr_reader :sincedb_time
18
+
19
+ # Archive an object in the Oracle Cloud Infrastructure (OCI) Object Storage.
20
+ # The archiving is performed based on the configuration specified in the `@archieve_after_read` attribute
21
+ # of the `ObjectStorageGetter` class.
22
+ #
23
+ # @param object [Object] The object containing metadata information. It represents a file in the OCI Object Storage.
24
+ def archieve_object(object)
25
+ return unless @archieve_after_read
26
+
27
+ parmeters = {
28
+ objectName: object.name,
29
+ storageTier: 'Archive'
30
+ }
31
+ @client.update_object_storage_tier(@namespace, @bucket_name, parmeters)
32
+ end
33
+
34
+ # Decodes the data of the given object using gzip decompression if the file has a `.gz` or `.gzip` extension.
35
+ # If the object's name does not match the pattern for gzip files, the data is returned as-is without decompression.
36
+ #
37
+ # @param object [Object] The object containing metadata information. It represents a file in the OCI Object Storage.
38
+ # @param response [Object] The response object containing the data of the object retrieved from the Object Storage.
39
+ # @return [String] The decoded data as a String.
40
+ def decode_gzip_file(object, response)
41
+ return response.data.to_s unless object.name.match(/gz(ip)?/i)
42
+
43
+ buffer = StringIO.new(response.data.to_s)
44
+ raw = Zlib::GzipReader.new(buffer)
45
+ raw.read
46
+ end
47
+
48
+ # Process the raw data using the specified Logstash codec and add metadata from the object to each event.
49
+ # After, includes this event on Logstash Queue.
50
+ #
51
+ # @param raw_data [String] The raw data to be processed. It represents the content of a file.
52
+ # @param object [Object] The object containing metadata information. It represents a file in the OCI Object Storage.
53
+ def process_data(raw_data, object)
54
+ meta = JSON.parse object.to_hash.to_json
55
+ @codec.decode(raw_data) do |event|
56
+ event.set('[@metadata][oci][object_storage]', meta)
57
+ @queue << event
58
+ end
59
+ end
60
+
61
+ # Download and process the data of the given object from the OCI Object Storage bucket.
62
+ #
63
+ # @param object [Object] The object containing metadata information. It represents a file in the OCI Object Storage.
64
+ def download_file(object)
65
+ response = @client.get_object(
66
+ @namespace,
67
+ @bucket_name,
68
+ object.name
69
+ )
70
+ raw_data = decode_gzip_file object, response
71
+ process_data raw_data, object
72
+ archieve_object object if @filter_strategy == 'archive'
73
+ rescue OCI::Errors::ServiceError => e
74
+ @logger.warn("The file #{object.name} cannot be downloaded: #{e} => #{object.to_hash}")
75
+ end
76
+
77
+ # Check whether the given object should be skipped or processed based on specific conditions.
78
+ #
79
+ # @param object [Object] The object containing metadata information. It represents a file in the OCI Object Storage.
80
+ # @param normalized_time [Time] The normalized time of the object's modification timestamp used for comparison.
81
+ def skip_file?(object, normalized_time)
82
+ if ['Archive'].include? object.storage_tier
83
+ true
84
+ elsif %w[Restoring Archived].include? object.archival_state
85
+ true
86
+ elsif (@sincedb_time > normalized_time) && (@filter_strategy == 'sincedb')
87
+ true
88
+ else
89
+ false
90
+ end
91
+ end
92
+
93
+ # Download and process filtered files from the OCI Object Storage bucket.
94
+ # Download and processing of objects are performed concurrently using a thread pool, with the number of threads
95
+ # specified by the `@threads` attribute of the `ObjectStorageGetter` class.
96
+ #
97
+ # @param buffer [Array] An array containing objects to be downloaded and processed. Each object represents a file.
98
+ def download_filtered_files(buffer)
99
+ time_buffer = []
100
+ pool = Thread.pool(@threads)
101
+ buffer.each do |object|
102
+ normalized_time = Time.parse(object.time_modified.to_s)
103
+ next if skip_file? object, normalized_time
104
+
105
+ time_buffer << normalized_time
106
+ @logger.info("Downloading file from #{object.name}")
107
+ pool.process { download_file object }
108
+ end
109
+ pool.shutdown
110
+ @sincedb_time = time_buffer.max unless time_buffer.empty? && (@filter_strategy == 'sincedb')
111
+ end
112
+
113
+ # Retrieve files from the OCI Object Storage bucket recursively based on specified parameters.
114
+ #
115
+ # @param parameters [Hash] A hash containing parameters for the OCI Object Storage API request.
116
+ # @param buffer [Array] An optional array that accumulates the retrieved objects. Each object represents a file.
117
+ # @return [Array] An array containing the retrieved objects after the recursive retrieval process.
118
+ def retrieve_files_recursive(parameters, buffer = [])
119
+ response = @client.list_objects(@namespace, @bucket_name, parameters)
120
+ buffer.push(*response.data.objects)
121
+ if response.data.next_start_with.nil?
122
+ @logger.debug('Nil pointer received!')
123
+ return buffer
124
+ end
125
+
126
+ @logger.info("Retriving next page: Last Page: #{@next_start} - Next Page: #{response.data.next_start_with}")
127
+ @next_start = response.data.next_start_with
128
+ parameters[:start] = @next_start
129
+ retrieve_files_recursive(parameters, buffer)
130
+ end
131
+
132
+ # Retrieve files from the OCI Object Storage bucket based on the specified prefix.
133
+ #
134
+ # @param prefix [String] An optional parameter to filter files based on that name prefix. Default is an empty string.
135
+ def retrieve_files(prefix = '')
136
+ parameters = {
137
+ prefix: prefix,
138
+ start: @next_start || '',
139
+ fields: 'name,timeCreated,timeModified,storageTier,archivalState'
140
+ }
141
+ buffer = retrieve_files_recursive parameters
142
+ download_filtered_files buffer
143
+ end
144
+
145
+ # Initialize a new instance of the ObjectStorageGetter class with the given parameters.
146
+ #
147
+ # Parameters:
148
+ # @param parameters [Hash] A hash containing configuration parameters for the ObjectStorageGetter instance.
149
+ # - :queue [LogStash::Util::WrappedSynchronousQueue] The queue to which the processed events will be sent.
150
+ # - :codec [Codec] The codec used to decode data from the retrieved objects in the OCI Object Storage bucket.
151
+ # - :namespace [String] The OCI Object Storage namespace associated with the bucket.
152
+ # - :bucket_name [String] The name of the OCI Object Storage bucket.
153
+ # - :credentials [OCI::ConfigFile::Config] The OCI configuration used for authentication.
154
+ # - :archieve_after_read [Boolean] A flag indicating whether the retrieved objects should be archived after read.
155
+ # - :filter_strategy [String] Strategy used for filtering files during retrieval. Can be `archive` or `sincedb`.
156
+ # - :sincedb_time [Time] Last modified timestamp recorded in the sincedb for filtering based on the 'sincedb'.
157
+ # - :logger [Logger] Logger instance used to log information, warnings, and errors during the retrieval process.
158
+ # - :threads [Integer] The number of threads used for concurrent processing of the retrieved objects.
159
+ def initialize(parameters)
160
+ @queue = parameters[:queue]
161
+ @codec = parameters[:codec]
162
+ @namespace = parameters[:namespace]
163
+ @bucket_name = parameters[:bucket_name]
164
+ @client = OCI::ObjectStorage::ObjectStorageClient.new(config: parameters[:credentials])
165
+ @archieve_after_read = parameters[:archieve_after_read]
166
+ @filter_strategy = parameters[:filter_strategy]
167
+ @sincedb_time = parameters[:sincedb_time] || Time.new(0)
168
+ @logger = parameters[:logger]
169
+ @threads = parameters[:threads]
170
+ @next_start = ''
171
+ end
172
+ end
173
+
174
+ # The LogStash module provides a namespace for all Logstash-related functionalities and classes.
175
+ # It encapsulates the components required for data ingestion, processing, and output in the Logstash pipeline.
176
+ # The module contains various submodules and classes that define inputs, filters, codecs, and outputs.
177
+ # These components work together to collect, filter, and transform data from various sources, enabling data
178
+ # ingestion into the Logstash pipeline.
179
+ module LogStash
180
+ # The LogStash::Inputs module is a submodule of the LogStash module, specifically responsible for handling data
181
+ # input in the Logstash pipeline. It provides a namespace for all input-related functionalities, including various
182
+ # input plugins that enable Logstash to collect data from different sources. Each input plugin represents a specific
183
+ # data source or input type, such as files, databases, HTTP endpoints, message queues, and more.
184
+ module Inputs
185
+ # The OciObjectStorage class is a Logstash input plugin that enables Logstash to read data
186
+ # from Oracle Cloud Infrastructure (OCI) Object Storage.
187
+ # It periodically fetches files from a specified OCI Object Storage bucket and forwards
188
+ # the data to the Logstash pipeline for further processing.
189
+ class OciObjectStorage < LogStash::Inputs::Base
190
+ config_name 'oci_object_storage'
191
+
192
+ # If undefined, Logstash will complain, even if codec is unused.
193
+ default :codec, 'plain'
194
+
195
+ config :namespace, validate: :string, required: true
196
+ config :bucket_name, validate: :string, required: true
197
+ config :config_path, validate: :string, required: true
198
+
199
+ config :archieve_after_read, validate: :boolean, required: false, default: true
200
+ config :filter_strategy, validate: :string, required: false, default: 'archive'
201
+ config :threads, validate: :number, required: false, default: ::LogStash::Config::CpuCoreStrategy.maximum
202
+ config :interval, validate: :number, default: 30
203
+ config :sincedb_path, validate: :string, default: nil
204
+
205
+ # Get the folder path for the sincedb file used by the OCI Object Storage input plugin in Logstash.
206
+ #
207
+ # @return [String] The folder path for the sincedb file specific to the OCI Object Storage input plugin.
208
+ def sincedb_folder
209
+ logstash_path = LogStash::SETTINGS.get_value('path.data')
210
+ dir = File.join(logstash_path, 'plugins', 'inputs', 'oci_object_storage')
211
+ FileUtils.mkdir_p(dir)
212
+ dir
213
+ end
214
+
215
+ # Get the full path to the sincedb file for the OCI Object Storage input plugin in Logstash.
216
+ #
217
+ # @return [String] The full path to the sincedb file specific to the OCI Object Storage input plugin.
218
+ def sincedb_file
219
+ digest = Digest::MD5.hexdigest("#{@namespace}+#{@bucket_name}+#{@prefix}")
220
+ path = File.join(sincedb_folder, "sincedb_#{digest}")
221
+ if ENV['HOME']
222
+ old = File.join(ENV['HOME'], ".sincedb_#{digest}")
223
+ if File.exist?(old)
224
+ logger.info('Migrating old sincedb in $HOME to {path.data}')
225
+ FileUtils.mv(old, path)
226
+ end
227
+ end
228
+ path
229
+ end
230
+
231
+ # The register method is a lifecycle method in the OCIObjectStorage input plugin for Logstash.
232
+ # It is called once during the plugin initialization phase and is responsible for setting up sincedb
233
+ # functionality when the 'sincedb' filter strategy is enabled.
234
+ def register
235
+ return unless filter_strategy == 'sincedb'
236
+
237
+ if @sincedb_path.nil?
238
+ @logger.info('Using default generated file for the sincedb', filename: sincedb_file)
239
+ @sincedb = SinceDB::File.new(sincedb_file)
240
+ else
241
+ @logger.info('Using the provided sincedb_path', sincedb_path: @sincedb_path)
242
+ @sincedb = SinceDB::File.new(@sincedb_path)
243
+ end
244
+
245
+ @sincedb_time = @sincedb.read
246
+ end
247
+
248
+ # The run method is a lifecycle method in the OCIObjectStorage input plugin for Logstash.
249
+ # It is responsible for the main execution of the OCIObjectStorage input plugin and the retrieval and
250
+ # processing of files from the OCI Object Storage bucket.
251
+ #
252
+ # @param queue [LogStash::Util::WrappedSynchronousQueue] The queue to which the processed events will be sent.
253
+ def run(queue)
254
+ @logger.info("Loading config file: #{@config_path}")
255
+ parameters = {
256
+ namespace: @namespace,
257
+ bucket_name: @bucket_name,
258
+ credentials: OCI::ConfigFileLoader.load_config(config_file_location: @config_path),
259
+ queue: queue,
260
+ codec: @codec,
261
+ archieve_after_read: @archieve_after_read,
262
+ filter_strategy: @filter_strategy,
263
+ sincedb_time: @sincedb_time,
264
+ threads: @threads,
265
+ logger: @logger
266
+ }
267
+ @client = ObjectStorageGetter.new(parameters)
268
+ until stop?
269
+ @client.retrieve_files
270
+ @sincedb.write(@client.sincedb_time) if filter_strategy == 'sincedb'
271
+ Stud.stoppable_sleep(@interval) { stop? }
272
+ end
273
+ end
274
+
275
+ # The stop method is a lifecycle method in the OCIObjectStorage input plugin for Logstash.
276
+ # It is responsible for stopping the execution of the OCIObjectStorage input plugin and performing
277
+ # any necessary cleanup tasks before exiting.
278
+ #
279
+ # @return [Boolean] Always returns true to signal that the Logstash pipeline should be stopped.
280
+ def stop
281
+ @sincedb.write(@client.sincedb_time) if filter_strategy == 'sincedb'
282
+ true
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ # The SinceDB module provides a namespace for classes related to managing since database (sincedb) functionality.
289
+ # The sincedb is a feature used to keep track of the progress in reading and processing files, ensuring that
290
+ # Logstash resumes where it left off in case of interruptions or restarts. The module contains classes and methods
291
+ # that handle reading and writing sincedb files, storing information about the last processed position in a file,
292
+ # and updating it as new data is ingested.
293
+ module SinceDB
294
+ # The SinceDB::File class represents a sincedb file used to track the progress of reading and processing files
295
+ # in Logstash. It provides methods to read and write sincedb information, which helps to maintain data integrity
296
+ # and support incremental data processing for file-based data sources in Logstash.
297
+ class File
298
+ # Constructor method to initialize a new instance of SinceDB::File.
299
+ #
300
+ # @param file [String] The path to the sincedb file to be managed by this instance.
301
+ def initialize(file)
302
+ @sincedb_path = file
303
+ end
304
+
305
+ # Read the sincedb file and return the last processed timestamp as a Time object.
306
+ #
307
+ # @return [Time] The last processed timestamp recorded in the sincedb file, represented as a Time object.
308
+ def read
309
+ if ::File.exist?(@sincedb_path)
310
+ content = ::File.read(@sincedb_path).chomp.strip
311
+ # If the file was created but we didn't have the time to write to it
312
+ content.empty? ? Time.new(0) : Time.parse(content)
313
+ else
314
+ Time.new(0)
315
+ end
316
+ end
317
+
318
+ # Write the provided timestamp to the sincedb file.
319
+ #
320
+ # @param since [Time] An optional parameter representing the timestamp. If not provided, Time.now is used.
321
+ def write(since = nil)
322
+ since = Time.now if since.nil?
323
+ ::File.open(@sincedb_path, 'w') { |file| file.write(since.to_s) }
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-input-oci'
3
+ s.version = '0.1.0'
4
+ s.licenses = ['Apache License (2.0)']
5
+ s.summary = "Collection of Logstash plugins that integrate with OCI."
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"
7
+ s.authors = ["Kalingth"]
8
+ s.email = 'wallace.faria@kalingth.com.br'
9
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
10
+ s.require_paths = ["lib"]
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
14
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
15
+
16
+ # Special flag to let us know this is actually a logstash plugin
17
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
18
+
19
+ # Gem dependencies
20
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
21
+ s.add_runtime_dependency 'logstash-codec-plain'
22
+ s.add_runtime_dependency 'oci', '~> 2.18'
23
+ s.add_runtime_dependency 'thread', '>= 0.2.0'
24
+ s.add_runtime_dependency 'stud', '>= 0.0.22'
25
+ s.add_development_dependency 'logstash-devutils'
26
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/devutils/rspec/shared_examples"
4
+ require "logstash/inputs/oci_object_storage"
5
+
6
+ describe LogStash::Inputs::OciObjectStorage do
7
+
8
+ it_behaves_like "an interruptible input plugin" do
9
+ let(:config) { { "interval" => 100 } }
10
+ end
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-oci
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kalingth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core-plugin-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.60'
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: '2.99'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.60'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ name: logstash-codec-plain
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: oci
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.18'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.18'
61
+ - !ruby/object:Gem::Dependency
62
+ name: thread
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 0.2.0
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 0.2.0
75
+ - !ruby/object:Gem::Dependency
76
+ name: stud
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 0.0.22
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 0.0.22
89
+ - !ruby/object:Gem::Dependency
90
+ name: logstash-devutils
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: This gem is a Logstash plugin required to be installed on top of the
104
+ Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
105
+ gem is not a stand-alone program
106
+ email: wallace.faria@kalingth.com.br
107
+ executables: []
108
+ extensions: []
109
+ extra_rdoc_files: []
110
+ files:
111
+ - CHANGELOG.md
112
+ - DEVELOPER.md
113
+ - Gemfile
114
+ - LICENSE
115
+ - NOTICE.TXT
116
+ - README.md
117
+ - lib/logstash/inputs/oci_object_storage.rb
118
+ - logstash-input-oci.gemspec
119
+ - spec/inputs/oci_object_storage.rb
120
+ homepage: http://www.elastic.co/guide/en/logstash/current/index.html
121
+ licenses:
122
+ - Apache License (2.0)
123
+ metadata:
124
+ logstash_plugin: 'true'
125
+ logstash_group: input
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubygems_version: 3.4.10
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Collection of Logstash plugins that integrate with OCI.
145
+ test_files:
146
+ - spec/inputs/oci_object_storage.rb