fluent-plugin-azure-queue 0.0.6.pre → 0.0.8.pre
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 +4 -4
- data/README.md +35 -0
- data/VERSION +1 -1
- data/lib/fluent/plugin/in_azure_event_hub_capture.rb +51 -25
- data/test/test_in_azure_event_hub_capture.rb +56 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dec8e5dd6a0f5973f5741702060efb40cc04b22
|
4
|
+
data.tar.gz: 0b1fb55a244ccc5a8a1ad390d09f1950eaf3e22c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4cf9f3f62e6213c4aeb421741bcde86fc4ecffef9a0b0b3efa0c0002390cb58e398c8187d698bab9b9f0babd01c531ea5a0273877cfbad7bbdb74cf505e4555
|
7
|
+
data.tar.gz: 6092c42d2fba01796c75085c1692e4f94696f37ba7314fdf38273d3cc309d6187fcf4562b44a46df7203f7b9f3dc865e0bb304248f0fa39d3bc9c568eb378e98
|
data/README.md
CHANGED
@@ -76,6 +76,7 @@ public static void Run(string[] hubMessages, ICollector<string> outputQueue, Tra
|
|
76
76
|
}
|
77
77
|
}
|
78
78
|
```
|
79
|
+
|
79
80
|
## azure_event_hub_capture Input Plugin
|
80
81
|
This plugin is designed to work with blobs stored to a container via [Azure Event Hubs Capture](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-capture-overview)
|
81
82
|
|
@@ -121,3 +122,37 @@ The time in seconds to sleep between fetching the blob list. Default 30
|
|
121
122
|
**lease_duration**
|
122
123
|
|
123
124
|
The time to lease the messages for. Default 60
|
125
|
+
|
126
|
+
**queue_lease_time**
|
127
|
+
|
128
|
+
The time to lease the queue message for. Default 60
|
129
|
+
|
130
|
+
**queue_name**
|
131
|
+
|
132
|
+
If set, get the blob names from a storage queue. Good if there are many blobs because listblobs is not reliable.
|
133
|
+
|
134
|
+
## Integration with Azure Event Hub
|
135
|
+
|
136
|
+
Use this azure function if you want to ingest event hub capture blob names from a storage queue.
|
137
|
+
|
138
|
+
```c#
|
139
|
+
// Blob trigger binding to a CloudBlockBlob
|
140
|
+
#r "Microsoft.WindowsAzure.Storage"
|
141
|
+
|
142
|
+
using Microsoft.WindowsAzure.Storage.Blob;
|
143
|
+
|
144
|
+
public static void Run(CloudBlockBlob myBlob, out BlobIdentifier outputQueueItem, TraceWriter log)
|
145
|
+
{
|
146
|
+
log.Info($"C# Blob trigger function Processed blob Name:{myBlob.Name} URI:{myBlob.StorageUri}");
|
147
|
+
BlobIdentifier blobId = new BlobIdentifier { Name = myBlob.Name, Container = myBlob.Container.Name };
|
148
|
+
//outputQueueItem.add(blobId);
|
149
|
+
outputQueueItem = blobId;
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
public class BlobIdentifier
|
154
|
+
{
|
155
|
+
public string Container { get; set; }
|
156
|
+
public string Name { get; set; }
|
157
|
+
}
|
158
|
+
```
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8.pre
|
@@ -20,6 +20,10 @@ module Fluent
|
|
20
20
|
config_param :fetch_interval, :integer, default: 30
|
21
21
|
desc 'The the lease duration on the blob in seconds'
|
22
22
|
config_param :lease_duration, :integer, default: 60
|
23
|
+
desc 'The the lease time on the messages in seconds'
|
24
|
+
config_param :queue_lease_time, :integer, default: 60
|
25
|
+
desc 'If set, get the blob names from a queue rather than the "list blobs" operation'
|
26
|
+
config_param :queue_name, :string, default: nil
|
23
27
|
|
24
28
|
def configure(conf)
|
25
29
|
super
|
@@ -30,9 +34,9 @@ module Fluent
|
|
30
34
|
if @lease_duration > 60 || @lease_duration < 15
|
31
35
|
raise Fluent::ConfigError, "fluent-plugin-azure-queue: 'lease_duration' parameter must be between 15 and 60: #{@lease_duration}"
|
32
36
|
end
|
33
|
-
@
|
37
|
+
@azure_client = Azure::Storage::Client.create(
|
34
38
|
:storage_account_name => @storage_account_name,
|
35
|
-
:storage_access_key => @storage_access_key)
|
39
|
+
:storage_access_key => @storage_access_key)
|
36
40
|
@running = true
|
37
41
|
@containers = container_names.split(',').map { |c| c.strip }
|
38
42
|
|
@@ -55,19 +59,10 @@ module Fluent
|
|
55
59
|
while @running
|
56
60
|
if Time.now > @next_fetch_time
|
57
61
|
@next_fetch_time = Time.now + @fetch_interval
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
log.info("Found #{blobs.count} unlocked blobs", container_name: container_name)
|
63
|
-
# Blobs come back with oldest first
|
64
|
-
blobs.each do |blob|
|
65
|
-
ingest_blob(container_name, blob)
|
66
|
-
end
|
67
|
-
rescue => e
|
68
|
-
log.warn(error: e)
|
69
|
-
log.warn_backtrace(e.backtrace)
|
70
|
-
end
|
62
|
+
if queue_name
|
63
|
+
ingest_from_queue
|
64
|
+
else
|
65
|
+
ingest_from_blob_list
|
71
66
|
end
|
72
67
|
else
|
73
68
|
sleep(@next_fetch_time - Time.now)
|
@@ -75,25 +70,56 @@ module Fluent
|
|
75
70
|
end
|
76
71
|
end
|
77
72
|
|
78
|
-
def
|
73
|
+
def ingest_from_blob_list
|
74
|
+
@containers.each do |container_name|
|
75
|
+
begin
|
76
|
+
blobs = @azure_client.blob_client.list_blobs(container_name)
|
77
|
+
blobs = blobs.select { |b| b.properties[:lease_status] == "unlocked" }
|
78
|
+
log.info("Found #{blobs.count} unlocked blobs", container_name: container_name)
|
79
|
+
# Blobs come back with oldest first
|
80
|
+
blobs.each do |blob|
|
81
|
+
ingest_blob(container_name, blob.name)
|
82
|
+
end
|
83
|
+
rescue => e
|
84
|
+
log.warn(error: e)
|
85
|
+
log.warn_backtrace(e.backtrace)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def ingest_from_queue
|
91
|
+
begin
|
92
|
+
blob_id_messages = @azure_client.queue_client.list_messages(@queue_name, @queue_lease_time, { number_of_messages: 32 })
|
93
|
+
blob_id_messages.each do |blob_id_message|
|
94
|
+
blob_id = JSON.parse(Base64.decode64(blob_id_message.message_text))
|
95
|
+
ingest_blob(blob_id["Container"], blob_id["Name"])
|
96
|
+
@azure_client.queue_client.delete_message(@queue_name, blob_id_message.id, blob_id_message.pop_receipt)
|
97
|
+
end
|
98
|
+
rescue => e
|
99
|
+
log.warn(error: e)
|
100
|
+
log.warn_backtrace(e.backtrace)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def ingest_blob(container_name, blob_name)
|
79
105
|
begin
|
80
|
-
lease_id = @blob_client.acquire_blob_lease(container_name,
|
81
|
-
log.info("Blob Leased", blob_name:
|
82
|
-
blob, blob_contents = @blob_client.get_blob(container_name,
|
106
|
+
lease_id = @azure_client.blob_client.acquire_blob_lease(container_name, blob_name, duration: @lease_duration)
|
107
|
+
log.info("Blob Leased", blob_name: blob_name)
|
108
|
+
blob, blob_contents = @azure_client.blob_client.get_blob(container_name, blob_name)
|
83
109
|
emit_blob_messages(blob_contents)
|
84
|
-
log.trace("Done Ingest blob", blob_name:
|
110
|
+
log.trace("Done Ingest blob", blob_name: blob_name)
|
85
111
|
begin
|
86
112
|
delete_blob(container_name, blob, lease_id)
|
87
|
-
log.debug("Blob deleted", blob_name:
|
113
|
+
log.debug("Blob deleted", blob_name: blob_name)
|
88
114
|
rescue Exception => e
|
89
|
-
log.warn("Records emmitted but blob not deleted", container_name: container_name, blob_name:
|
115
|
+
log.warn("Records emmitted but blob not deleted", container_name: container_name, blob_name: blob_name, error: e)
|
90
116
|
log.warn_backtrace(e.backtrace)
|
91
117
|
end
|
92
118
|
rescue Azure::Core::Http::HTTPError => e
|
93
119
|
if e.status_code == 409
|
94
|
-
log.trace("Blob already leased", blob_name:
|
120
|
+
log.trace("Blob already leased", blob_name: blob_name)
|
95
121
|
elsif e.status_code == 404
|
96
|
-
log.trace("Blob already deleted", blob_name:
|
122
|
+
log.trace("Blob already deleted", blob_name: blob_name)
|
97
123
|
else
|
98
124
|
log.warn("Error occurred while ingesting blob", error: e)
|
99
125
|
log.warn_backtrace(e.backtrace)
|
@@ -127,7 +153,7 @@ module Fluent
|
|
127
153
|
def delete_blob(container_name, blob, lease_id)
|
128
154
|
# Hack because 'delete_blob' doesn't support lease_id yet
|
129
155
|
Azure::Storage::Service::StorageService.register_request_callback { |headers| headers["x-ms-lease-id"] = lease_id }
|
130
|
-
@blob_client.delete_blob(container_name, blob.name)
|
156
|
+
@azure_client.blob_client.delete_blob(container_name, blob.name)
|
131
157
|
Azure::Storage::Service::StorageService.register_request_callback { |headers| headers }
|
132
158
|
end
|
133
159
|
end
|
@@ -11,7 +11,7 @@ class AzureEventHubCaptureInputTest < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
LIST_CONFIG = %[
|
15
15
|
tag test_tag
|
16
16
|
storage_account_name test_storage_account_name
|
17
17
|
storage_access_key test_storage_access_key
|
@@ -19,13 +19,24 @@ class AzureEventHubCaptureInputTest < Test::Unit::TestCase
|
|
19
19
|
fetch_interval 1
|
20
20
|
]
|
21
21
|
|
22
|
-
|
22
|
+
QUEUE_CONFIG = %[
|
23
|
+
tag test_tag
|
24
|
+
storage_account_name test_storage_account_name
|
25
|
+
storage_access_key test_storage_access_key
|
26
|
+
container_names test_container_name
|
27
|
+
fetch_interval 1
|
28
|
+
queue_lease_time 30
|
29
|
+
queue_name test_queue_name
|
30
|
+
]
|
31
|
+
|
32
|
+
def create_driver(conf = LIST_CONFIG)
|
23
33
|
d = Fluent::Test::InputTestDriver.new(Fluent::AzureEventHubCaptureInput)
|
24
34
|
d.configure(conf)
|
25
35
|
d
|
26
36
|
end
|
27
37
|
|
28
38
|
Struct.new("Blob", :name, :properties)
|
39
|
+
Struct.new("QueueMessage", :id, :pop_receipt, :message_text)
|
29
40
|
|
30
41
|
def test_configure
|
31
42
|
d = create_driver
|
@@ -38,14 +49,15 @@ class AzureEventHubCaptureInputTest < Test::Unit::TestCase
|
|
38
49
|
|
39
50
|
def setup_mocks(driver)
|
40
51
|
blob_client = flexmock("blob_client")
|
41
|
-
|
52
|
+
queue_client = flexmock("queue_client")
|
53
|
+
client = flexmock("client", :blob_client => blob_client, :queue_client => queue_client)
|
42
54
|
flexmock(Azure::Storage::Client, :create => client)
|
43
|
-
blob_client
|
55
|
+
[blob_client, queue_client]
|
44
56
|
end
|
45
57
|
|
46
|
-
def
|
58
|
+
def test_list_no_blobs
|
47
59
|
d = create_driver
|
48
|
-
blob_client = setup_mocks(d)
|
60
|
+
blob_client, queue_client = setup_mocks(d)
|
49
61
|
blob_client.should_receive(:list_blobs).with(d.instance.container_names).and_return([]).once
|
50
62
|
flexmock(d.instance).should_receive(:ingest_blob).never()
|
51
63
|
d.run do
|
@@ -53,10 +65,10 @@ class AzureEventHubCaptureInputTest < Test::Unit::TestCase
|
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
56
|
-
def
|
68
|
+
def test_list_two_blobs
|
57
69
|
d = create_driver
|
58
70
|
blobs = [Struct::Blob.new("test1", lease_status: "unlocked"), Struct::Blob.new("test2", lease_status: "unlocked")]
|
59
|
-
blob_client = setup_mocks(d)
|
71
|
+
blob_client, queue_client = setup_mocks(d)
|
60
72
|
blob_client.should_receive(:list_blobs).with(d.instance.container_names).and_return(blobs).once
|
61
73
|
plugin = flexmock(d.instance)
|
62
74
|
plugin.should_receive(:ingest_blob).with(d.instance.container_names, blobs[0]).once()
|
@@ -66,10 +78,44 @@ class AzureEventHubCaptureInputTest < Test::Unit::TestCase
|
|
66
78
|
end
|
67
79
|
end
|
68
80
|
|
81
|
+
def test_queue_no_blobs
|
82
|
+
d = create_driver(QUEUE_CONFIG)
|
83
|
+
blob_client, queue_client = setup_mocks(d)
|
84
|
+
queue_client.should_receive(:list_messages).with(
|
85
|
+
d.instance.queue_name,
|
86
|
+
d.instance.queue_lease_time,
|
87
|
+
{ number_of_messages: 32}).and_return([]).once
|
88
|
+
flexmock(d.instance).should_receive(:ingest_blob).never()
|
89
|
+
d.run do
|
90
|
+
sleep 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_queue_two_blobs
|
95
|
+
d = create_driver(QUEUE_CONFIG)
|
96
|
+
blob_id1 = { "Name" => "test1", "Container" => "container1" }
|
97
|
+
blob_id2 = { "Name" => "test2", "Container" => "container2" }
|
98
|
+
blobs_id_messages = [ Struct::QueueMessage.new(1, 99, Base64.encode64(blob_id1.to_json)),
|
99
|
+
Struct::QueueMessage.new(2, 299, Base64.encode64(blob_id2.to_json))]
|
100
|
+
blob_client, queue_client = setup_mocks(d)
|
101
|
+
queue_client.should_receive(:list_messages).with(
|
102
|
+
d.instance.queue_name,
|
103
|
+
d.instance.queue_lease_time,
|
104
|
+
{ number_of_messages: 32}).and_return(blobs_id_messages).once
|
105
|
+
plugin = flexmock(d.instance)
|
106
|
+
plugin.should_receive(:ingest_blob).with(blob_id1["Container"], blob_id1["Name"]).once()
|
107
|
+
plugin.should_receive(:ingest_blob).with(blob_id2["Container"], blob_id2["Name"]).once()
|
108
|
+
queue_client.should_receive(:delete_message).with(d.instance.queue_name, blobs_id_messages[0].id, blobs_id_messages[0].pop_receipt).once
|
109
|
+
queue_client.should_receive(:delete_message).with(d.instance.queue_name, blobs_id_messages[1].id, blobs_id_messages[1].pop_receipt).once
|
110
|
+
d.run do
|
111
|
+
sleep 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
69
115
|
def test_ingest_blob
|
70
116
|
d = create_driver
|
71
117
|
blob = Struct::Blob.new("test1", lease_status: "unlocked")
|
72
|
-
blob_client = setup_mocks(d)
|
118
|
+
blob_client, queue_client = setup_mocks(d)
|
73
119
|
plugin = flexmock(d.instance)
|
74
120
|
lease_id = "123"
|
75
121
|
blob_client.should_receive(:acquire_blob_lease).with(d.instance.container_names, blob.name, duration: d.instance.lease_duration).and_return(lease_id).once
|
@@ -85,7 +131,7 @@ class AzureEventHubCaptureInputTest < Test::Unit::TestCase
|
|
85
131
|
|
86
132
|
def test_emit_blob_messages
|
87
133
|
d = create_driver
|
88
|
-
setup_mocks(d)
|
134
|
+
blob_client, queue_client = setup_mocks(d)
|
89
135
|
test_payload = flexmock("test_payload")
|
90
136
|
buffer = flexmock("buffer")
|
91
137
|
flexmock(StringIO).should_receive(:new).and_return(buffer)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-azure-queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Bonebrake
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|