fluent-plugin-azure-queue 0.0.6.pre → 0.0.8.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|