triglav-agent-vertica 1.0.0.rc1
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +138 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/example/config.yml +42 -0
- data/example/example.env +8 -0
- data/exe/triglav-agent-vertica +13 -0
- data/lib/triglav/agent/vertica.rb +11 -0
- data/lib/triglav/agent/vertica/connection.rb +65 -0
- data/lib/triglav/agent/vertica/monitor.rb +305 -0
- data/lib/triglav/agent/vertica/version.rb +7 -0
- data/prepare.sh +3 -0
- data/start.sh +8 -0
- data/triglav-agent-vertica.gemspec +33 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 60cfd7322991b2deda4453644f24760066d26bed
|
4
|
+
data.tar.gz: 28993b067403c1caeac88eda684f03b47b2736a4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 666c7ed4db2c4b8f660131d049708b146939fab77d6713af173258c59da99b4393a6efa1c1bd28326418139244c813849dfed746ef3ec01e0303786813fea027
|
7
|
+
data.tar.gz: 1532c2898a58e4beaec1cb91ab1317f08b550e2a5de1aec7c04164e5894206b17dbd806587ece7c050d2bf46ac5d713008ee7a22b7da8899bead717a077024f3
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at sonots@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Triglav Team
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# Triglav::Agent::Vertica
|
2
|
+
|
3
|
+
Triglav Agent for Vertica
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
* Ruby >= 2.3.0
|
8
|
+
|
9
|
+
## Prerequisites
|
10
|
+
|
11
|
+
* Vertica table must have a DATE column for `daily` resource monitor
|
12
|
+
* Vertica table must have a TIMESTAMP or TIMESTAMPTZ column for `hourly` resource monitor
|
13
|
+
* Vertica view is not supported (since `epoch` column can not be retrieved)
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'triglav-agent-vertica'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install triglav-agent-vertica
|
30
|
+
|
31
|
+
## CLI
|
32
|
+
|
33
|
+
```
|
34
|
+
Usage: triglav-agent-vertica [options]
|
35
|
+
-c, --config VALUE Config file (default: config.yml)
|
36
|
+
-s, --status VALUE Status stroage file (default: status.yml)
|
37
|
+
-t, --token VALUE Triglav access token storage file (default: token.yml)
|
38
|
+
--dotenv Load environment variables from .env file (default: false)
|
39
|
+
-h, --help help
|
40
|
+
--log VALUE Log path (default: STDOUT)
|
41
|
+
--log-level VALUE Log level (default: info)
|
42
|
+
```
|
43
|
+
|
44
|
+
Run as:
|
45
|
+
|
46
|
+
```
|
47
|
+
TRIGLAV_ENV=development bundle exec triglav-agent-vertica --dotenv -c config.yml
|
48
|
+
```
|
49
|
+
|
50
|
+
## Configuration
|
51
|
+
|
52
|
+
Prepare config.yml as [example/config.yml](./example/config.yml).
|
53
|
+
|
54
|
+
You can use erb template. You may load environment variables from .env file with `--dotenv` option as an [example/example.env](./example/example.env) file shows.
|
55
|
+
|
56
|
+
### serverengine section
|
57
|
+
|
58
|
+
You can specify any [serverengine](https://github.com/fluent/serverengine) options at this section
|
59
|
+
|
60
|
+
### triglav section
|
61
|
+
|
62
|
+
Specify triglav api url, and a credential to authenticate.
|
63
|
+
|
64
|
+
The access token obtained is stored into a token storage file (--token option).
|
65
|
+
|
66
|
+
### vertica section
|
67
|
+
|
68
|
+
This section is the special section for triglav-agent-vertica.
|
69
|
+
|
70
|
+
* **monitor_interval**: The interval to watch tables (number, default: 60)
|
71
|
+
* **connection_info**: key-value pairs of vertica connection info where keys are resource URI pattern in regular expression, and values are connection infomation
|
72
|
+
|
73
|
+
### Specification of Resource URI
|
74
|
+
|
75
|
+
Resource URI must be a form of:
|
76
|
+
|
77
|
+
```
|
78
|
+
vertica://#{host}:#{port}/#{db}/#{schema}/#{table}
|
79
|
+
```
|
80
|
+
|
81
|
+
URI also accepts query parameters of `date`, `timestamp`, and `where`.
|
82
|
+
|
83
|
+
* **date** (string): date column name (default: `d`)
|
84
|
+
* **timestamp** (string): timestamp column name (defualt: `t`)
|
85
|
+
* **where** (hash): where conditions (default: nil)
|
86
|
+
* A value looks like an integer string is treated as an integer such as `1`
|
87
|
+
* If you want to treat it as as string, surround with double quote or single quote such as `'0'`
|
88
|
+
* A value does not look like an integer is treated as a string such as `foo`
|
89
|
+
* Only equality operator is supported now
|
90
|
+
|
91
|
+
ex)
|
92
|
+
|
93
|
+
```
|
94
|
+
?date=d×tamp=t&where[id]=0&where[d]=2016-12-30
|
95
|
+
```
|
96
|
+
|
97
|
+
## How it behaves
|
98
|
+
|
99
|
+
1. Authenticate with triglav
|
100
|
+
* Store the access token into the token storage file
|
101
|
+
* Read the token from the token storage file next time
|
102
|
+
* Refresh the access token if it is expired
|
103
|
+
2. Repeat followings in `monitor_interval` seconds:
|
104
|
+
3. Obtain resource (table) lists of the specified prefix (keys of connection_info) from triglav.
|
105
|
+
4. Connect to vertica with an appropriate connection info for a resource uri, and find tables which are newer than last check.
|
106
|
+
5. Store checking information into the status storage file for the next time check.
|
107
|
+
|
108
|
+
## Development
|
109
|
+
|
110
|
+
### Prepare
|
111
|
+
|
112
|
+
```
|
113
|
+
./prepare.sh
|
114
|
+
```
|
115
|
+
|
116
|
+
Edit `.env` or `config.yml` file directly.
|
117
|
+
|
118
|
+
### Start
|
119
|
+
|
120
|
+
Start up triglav api on localhost.
|
121
|
+
|
122
|
+
Run triglav-anget-vertica as:
|
123
|
+
|
124
|
+
```
|
125
|
+
TRIGLAV_ENV=development bundle exec triglav-agent-vertica --dotenv --debug -c example/config.yml
|
126
|
+
```
|
127
|
+
|
128
|
+
The debug mode with --debug option ignores the `last_epoch` value in status file.
|
129
|
+
|
130
|
+
## Contributing
|
131
|
+
|
132
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/triglav-workflow/triglav-agent-vertica. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
133
|
+
|
134
|
+
|
135
|
+
## License
|
136
|
+
|
137
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
138
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
desc 'Run test_unit based test'
|
5
|
+
Rake::TestTask.new(:test) do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.test_files = Dir["test/**/test_*.rb"].sort
|
8
|
+
t.verbose = false
|
9
|
+
t.warning = false
|
10
|
+
end
|
11
|
+
task :default => :test
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "triglav/agent/vertica"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/example/config.yml
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
serverengine:
|
3
|
+
log: 'STDOUT'
|
4
|
+
log_level: 'debug'
|
5
|
+
log_rotate_age: 5
|
6
|
+
log_rotate_size: 10485760
|
7
|
+
triglav:
|
8
|
+
url: <%= ENV['TRIGLAV_URL'] || 'http://localhost:7800' %>
|
9
|
+
credential:
|
10
|
+
username: <%= ENV['TRIGLAV_USERNAME'] || 'triglav_test' %>
|
11
|
+
password: <%= ENV['TRIGLAV_PASSWORD'] || 'triglav_test' %>
|
12
|
+
authenticator: local
|
13
|
+
timeout: 60
|
14
|
+
debugging: false
|
15
|
+
retries: 3
|
16
|
+
retry_interval: 3 # sec
|
17
|
+
vertica:
|
18
|
+
monitor_interval: 5
|
19
|
+
parallel_size: 1 # default: 1
|
20
|
+
parallel_type: 'thread' # thread or process. default: thread
|
21
|
+
connection_pool_size: 1 # default: same with parallel.size
|
22
|
+
connection_pool_timeout: 60 # sec. default: 60
|
23
|
+
date_column: d
|
24
|
+
timestamp_column: t
|
25
|
+
interruptable: true
|
26
|
+
read_timeout: 5
|
27
|
+
connection_info:
|
28
|
+
"vertica://":
|
29
|
+
host: <%= ENV['VERTICA_HOST'] %>
|
30
|
+
port: <%= ENV['VERTICA_PORT'] %>
|
31
|
+
database: <%= ENV['VERTICA_DATABASE'] %>
|
32
|
+
user: <%= ENV['VERTICA_USER'] %>
|
33
|
+
password: <%= ENV['VERTICA_PASSWORD'] %>
|
34
|
+
resource_pool: <%= ENV['VERTICA_RESOURCE_POOL'] %>
|
35
|
+
interruptable: true
|
36
|
+
read_timeout: 5
|
37
|
+
|
38
|
+
development:
|
39
|
+
<<: *defaults
|
40
|
+
|
41
|
+
test:
|
42
|
+
<<: *defaults
|
data/example/example.env
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'triglav/agent/vertica'
|
4
|
+
Triglav::Agent::Configuration.configure do |config|
|
5
|
+
config.name = :vertica
|
6
|
+
# config.cli_class = Triglav::Agent::Vertica::CLI
|
7
|
+
# config.setting_class = Triglav::Agent::Vertica::Setting
|
8
|
+
# config.worker_module = Triglav::Agent::Vertica::Worker
|
9
|
+
# config.processor_class = Triglav::Agent::Vertica::Processor
|
10
|
+
config.monitor_class = Triglav::Agent::Vertica::Monitor
|
11
|
+
config.connection_class = Triglav::Agent::Vertica::Connection
|
12
|
+
end
|
13
|
+
Triglav::Agent::Configuration.cli_class.new.run
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'triglav/agent/base/connection'
|
2
|
+
require 'vertica'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Triglav::Agent
|
6
|
+
module Vertica
|
7
|
+
class Connection < Base::Connection
|
8
|
+
attr_reader :connection_info
|
9
|
+
|
10
|
+
def initialize(connection_info)
|
11
|
+
@connection_info = connection_info
|
12
|
+
end
|
13
|
+
|
14
|
+
def close
|
15
|
+
@connection.close rescue nil if @connection
|
16
|
+
end
|
17
|
+
|
18
|
+
def query(sql)
|
19
|
+
connection.query(sql)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def connection
|
25
|
+
return @connection if @connection
|
26
|
+
begin
|
27
|
+
@connection = ::Vertica.connect(connection_params)
|
28
|
+
rescue => e
|
29
|
+
$logger.error { "Failed to connect #{connection_info[:host]}:#{connection_info[:port]} with #{connection_info[:user]}" }
|
30
|
+
raise e
|
31
|
+
end
|
32
|
+
$logger.info { "Connected to #{connection_info[:host]}:#{connection_info[:port]}" }
|
33
|
+
set_resource_pool
|
34
|
+
set_memorycap
|
35
|
+
@connection
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_resource_pool
|
39
|
+
if @connection_info[:resource_pool] and !@connection_info[:resource_pool].empty?
|
40
|
+
@connection.query("set session resource_pool = '#{@connection_info[:resource_pool]}'")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_memorycap
|
45
|
+
if @connection_info[:memorycap] and !@connection_info[:memorycap].empty?
|
46
|
+
@connection.query("set session memorycap = '#{@connection_info[:memorycap]}'")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def connection_params
|
51
|
+
params = @connection_info.dup
|
52
|
+
params.delete(:resource_pool)
|
53
|
+
params.delete(:memorycap)
|
54
|
+
params.merge!(global_connection_params)
|
55
|
+
end
|
56
|
+
|
57
|
+
def global_connection_params
|
58
|
+
params = {}
|
59
|
+
params[:interruptable] = $setting.dig(:vertica, :interruptable) if $setting[:vertica].has_key?(:interruptable)
|
60
|
+
params[:read_timeout] = $setting.dig(:vertica, :read_timeout) if $setting[:vertica].has_key?(:read_timeout)
|
61
|
+
params
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
require 'triglav/agent/base/monitor'
|
2
|
+
require 'vertica'
|
3
|
+
require 'uri'
|
4
|
+
require 'cgi'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'rack/utils'
|
7
|
+
|
8
|
+
module Triglav::Agent
|
9
|
+
module Vertica
|
10
|
+
class Monitor < Base::Monitor
|
11
|
+
attr_reader :connection, :resource_uri_prefix, :resource, :periodic_last_epoch, :singular_last_epoch
|
12
|
+
|
13
|
+
# @param [Triglav::Agent::Vertica::Connection] connection
|
14
|
+
# @param [String] resource_uri_prefix
|
15
|
+
# @param [TriglavClient::ResourceResponse] resource
|
16
|
+
# resource:
|
17
|
+
# uri: vertica://host/database/schema/table
|
18
|
+
# unit: 'daily', 'hourly', 'singular', or their combinations such as 'singular,daily,hourly'
|
19
|
+
# timezone: '+09:00'
|
20
|
+
# span_in_days: 32
|
21
|
+
#
|
22
|
+
# View is not supported
|
23
|
+
def initialize(connection, resource_uri_prefix, resource)
|
24
|
+
@connection = connection
|
25
|
+
@resource_uri_prefix = resource_uri_prefix
|
26
|
+
@resource = resource
|
27
|
+
@status = Triglav::Agent::Status.new(resource_uri_prefix, resource.uri)
|
28
|
+
@periodic_last_epoch = $setting.debug? ? 0 : get_from_status_file(:periodic_last_epoch)
|
29
|
+
@singular_last_epoch = $setting.debug? ? 0 : get_from_status_file(:singular_last_epoch)
|
30
|
+
end
|
31
|
+
|
32
|
+
def process
|
33
|
+
unless resource_valid?
|
34
|
+
$logger.warn { "Broken resource: #{resource.to_s}" }
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
$logger.debug {
|
39
|
+
msgs = ["Start process #{resource.uri}"]
|
40
|
+
msgs << "periodic_last_epoch:#{periodic_last_epoch}" if periodic_last_epoch
|
41
|
+
msgs << "singular_last_epoch:#{singular_last_epoch}" if singular_last_epoch
|
42
|
+
msgs.join(', ')
|
43
|
+
}
|
44
|
+
|
45
|
+
if periodic?
|
46
|
+
periodic_events, new_periodic_last_epoch = get_periodic_events
|
47
|
+
events = periodic_events || []
|
48
|
+
end
|
49
|
+
if singular?
|
50
|
+
singular_events, new_singular_last_epoch = get_singular_events
|
51
|
+
events.nil? ? (events = singular_events) : events.concat(singular_events || [])
|
52
|
+
end
|
53
|
+
|
54
|
+
$logger.debug {
|
55
|
+
msgs = ["Finish process #{resource.uri}"]
|
56
|
+
msgs << "periodic_last_epoch:#{periodic_last_epoch}" if periodic_last_epoch
|
57
|
+
msgs << "singular_last_epoch:#{singular_last_epoch}" if singular_last_epoch
|
58
|
+
msgs << "new_periodic_last_epoch:#{new_periodic_last_epoch}" if new_periodic_last_epoch
|
59
|
+
msgs << "new_singular_last_epoch:#{new_singular_last_epoch}" if new_singular_last_epoch
|
60
|
+
msgs.join(', ')
|
61
|
+
}
|
62
|
+
|
63
|
+
return nil if events.nil? || events.empty?
|
64
|
+
yield(events) if block_given? # send_message
|
65
|
+
update_status_file(:periodic_last_epoch, new_periodic_last_epoch) if new_periodic_last_epoch
|
66
|
+
update_status_file(:singular_last_epoch, new_singular_last_epoch) if new_singular_last_epoch
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_periodic_events
|
71
|
+
if hourly?
|
72
|
+
events, new_last_epoch, rows = get_hourly_events
|
73
|
+
if daily?
|
74
|
+
daily_events = build_daily_events_from_hourly(rows)
|
75
|
+
events.concat(daily_events)
|
76
|
+
end
|
77
|
+
[events, new_last_epoch]
|
78
|
+
elsif daily?
|
79
|
+
get_daily_events
|
80
|
+
else
|
81
|
+
raise
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_singular_events
|
86
|
+
sql = "select " \
|
87
|
+
"NULL AS d, NULL AS h, max(epoch) " \
|
88
|
+
"from #{q_db}.#{q_schema}.#{q_table} " \
|
89
|
+
"#{q_where.empty? ? '' : "where #{q_where} "}" \
|
90
|
+
"having max(epoch) > #{q_singular_last_epoch}"
|
91
|
+
query_and_get_events(:singular, sql)
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_hourly_events
|
95
|
+
sql = "select " \
|
96
|
+
"#{q_date} AS d, DATE_PART('hour', #{q_timestamp}) AS h, max(epoch) " \
|
97
|
+
"from #{q_db}.#{q_schema}.#{q_table} " \
|
98
|
+
"where #{q_date} IN ('#{dates.join("','")}') " \
|
99
|
+
"#{q_where.empty? ? '' : "AND #{q_where} "}" \
|
100
|
+
"group by d, h having max(epoch) > #{q_periodic_last_epoch} " \
|
101
|
+
"order by d, h"
|
102
|
+
query_and_get_events(:hourly, sql)
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_daily_events
|
106
|
+
sql = "select " \
|
107
|
+
"#{q_date} AS d, 0 AS h, max(epoch) " \
|
108
|
+
"from #{q_db}.#{q_schema}.#{q_table} " \
|
109
|
+
"where #{q_date} IN ('#{dates.join("','")}') " \
|
110
|
+
"#{q_where.empty? ? '' : "AND #{q_where} "}" \
|
111
|
+
"group by d having max(epoch) > #{q_periodic_last_epoch} " \
|
112
|
+
"order by d"
|
113
|
+
query_and_get_events(:daily, sql)
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def query_and_get_events(unit, sql)
|
119
|
+
$logger.debug { "Query: #{sql}" }
|
120
|
+
rows = connection.query(sql)
|
121
|
+
events = build_events(unit, rows)
|
122
|
+
new_last_epoch = build_latest_epoch(rows)
|
123
|
+
[events, new_last_epoch, rows]
|
124
|
+
rescue ::Vertica::Error::QueryError => e
|
125
|
+
$logger.warn { "#{e.class} #{e.message}" } # e.message includes sql
|
126
|
+
nil
|
127
|
+
rescue ::Vertica::Error::TimedOutError => e
|
128
|
+
$logger.warn { "#{e.class} #{e.message} SQL:#{sql}" }
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def update_status_file(key, last_epoch)
|
133
|
+
@status.set(key.to_sym, last_epoch)
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_from_status_file(key)
|
137
|
+
@status.getsetnx(key.to_sym, get_current_epoch)
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_current_epoch
|
141
|
+
connection.query('select GET_CURRENT_EPOCH()').first.first
|
142
|
+
end
|
143
|
+
|
144
|
+
def resource_valid?
|
145
|
+
resource_unit_valid? && !resource.timezone.nil? && !resource.span_in_days.nil?
|
146
|
+
end
|
147
|
+
|
148
|
+
def resource_unit_valid?
|
149
|
+
resource.unit.split(',').each do |item|
|
150
|
+
return false unless %w[singular daily hourly].include?(item)
|
151
|
+
end
|
152
|
+
true
|
153
|
+
end
|
154
|
+
|
155
|
+
def hourly?
|
156
|
+
return @is_hourly unless @is_hourly.nil?
|
157
|
+
@is_hourly = resource.unit.include?('hourly')
|
158
|
+
end
|
159
|
+
|
160
|
+
def daily?
|
161
|
+
return @is_daily unless @is_daily.nil?
|
162
|
+
@is_daily = resource.unit.include?('daily')
|
163
|
+
end
|
164
|
+
|
165
|
+
def singular?
|
166
|
+
return @is_singular unless @is_singular.nil?
|
167
|
+
@is_singular = resource.unit.include?('singular')
|
168
|
+
end
|
169
|
+
|
170
|
+
def periodic?
|
171
|
+
hourly? or daily?
|
172
|
+
end
|
173
|
+
|
174
|
+
def dates
|
175
|
+
return @dates if @dates
|
176
|
+
now = Time.now.localtime(resource.timezone)
|
177
|
+
@dates = resource.span_in_days.times.map do |i|
|
178
|
+
(now - (i * 86000)).strftime('%Y-%m-%d')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def build_latest_epoch(rows)
|
183
|
+
rows.map {|row| row[2] }.max
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_events(unit, rows)
|
187
|
+
rows.map do |row|
|
188
|
+
date, hour, epoch = row[0], row[1], row[2]
|
189
|
+
{
|
190
|
+
uuid: SecureRandom.uuid,
|
191
|
+
resource_uri: resource.uri,
|
192
|
+
resource_unit: unit.to_s,
|
193
|
+
resource_time: date_hour_to_i(date, hour, resource.timezone),
|
194
|
+
resource_timezone: resource.timezone,
|
195
|
+
payload: (date ? {d: date.to_s, h: hour.to_i} : {}).merge!(epoch: epoch).to_json,
|
196
|
+
}
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def build_daily_events_from_hourly(rows)
|
201
|
+
max_epoch_of = {}
|
202
|
+
rows.each do |row|
|
203
|
+
date, hour, epoch = row[0], row[1], row[2]
|
204
|
+
max_epoch_of[date] = [epoch, max_epoch_of[date] || 0].max
|
205
|
+
end
|
206
|
+
daily_events = max_epoch_of.map do |date, epoch|
|
207
|
+
{
|
208
|
+
uuid: SecureRandom.uuid,
|
209
|
+
resource_uri: resource.uri,
|
210
|
+
resource_unit: 'daily',
|
211
|
+
resource_time: date_hour_to_i(date, 0, resource.timezone),
|
212
|
+
resource_timezone: resource.timezone,
|
213
|
+
payload: {d: date.to_s, h: 0, epoch: epoch}.to_json,
|
214
|
+
}
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def date_hour_to_i(date, hour, timezone)
|
219
|
+
return 0 if date.nil?
|
220
|
+
Time.strptime("#{date.strftime("%Y-%m-%d")} #{hour.to_i} #{timezone}", '%Y-%m-%d %H %z').to_i
|
221
|
+
end
|
222
|
+
|
223
|
+
def q_periodic_last_epoch
|
224
|
+
@q_periodic_last_epoch ||= ::Vertica.quote(periodic_last_epoch)
|
225
|
+
end
|
226
|
+
|
227
|
+
def q_singular_last_epoch
|
228
|
+
@q_singular_last_epoch ||= ::Vertica.quote(singular_last_epoch)
|
229
|
+
end
|
230
|
+
|
231
|
+
def parsed_uri
|
232
|
+
@parsed_uri ||= URI.parse(resource.uri)
|
233
|
+
end
|
234
|
+
|
235
|
+
def parsed_query
|
236
|
+
@parsed_query ||= Rack::Utils.parse_nested_query(parsed_uri.query)
|
237
|
+
end
|
238
|
+
|
239
|
+
def db
|
240
|
+
@db ||= parsed_uri.path[1..-1].split('/')[0]
|
241
|
+
end
|
242
|
+
|
243
|
+
def schema
|
244
|
+
@schema ||= parsed_uri.path[1..-1].split('/')[1]
|
245
|
+
end
|
246
|
+
|
247
|
+
def table
|
248
|
+
@table ||= parsed_uri.path[1..-1].split('/')[2]
|
249
|
+
end
|
250
|
+
|
251
|
+
def date_column
|
252
|
+
parsed_query['date'] || $setting.dig(:vertica, :date_column) || 'd'
|
253
|
+
end
|
254
|
+
|
255
|
+
def timestamp_column
|
256
|
+
parsed_query['timestamp'] || $setting.dig(:vertica, :timestamp_column) || 't'
|
257
|
+
end
|
258
|
+
|
259
|
+
def where
|
260
|
+
parsed_query['where'] || {}
|
261
|
+
end
|
262
|
+
|
263
|
+
def q_db
|
264
|
+
@q_db ||= ::Vertica.quote_identifier(db)
|
265
|
+
end
|
266
|
+
|
267
|
+
def q_schema
|
268
|
+
@q_schema ||= ::Vertica.quote_identifier(schema)
|
269
|
+
end
|
270
|
+
|
271
|
+
def q_table
|
272
|
+
@q_table ||= ::Vertica.quote_identifier(table)
|
273
|
+
end
|
274
|
+
|
275
|
+
def q_date
|
276
|
+
@q_date ||= ::Vertica.quote_identifier(date_column)
|
277
|
+
end
|
278
|
+
|
279
|
+
def q_timestamp
|
280
|
+
@q_timestamp ||= ::Vertica.quote_identifier(timestamp_column)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Value specification:
|
284
|
+
# * A value looks like an integer string is treated as an integer.
|
285
|
+
# * If you want to treat it as as string, surround with double quote or single quote.
|
286
|
+
# * A value does not look like an integer is treated as a string.
|
287
|
+
# Operator specification:
|
288
|
+
# * Only equality is supported now
|
289
|
+
def q_where
|
290
|
+
@q_where ||= where.map do |col, val|
|
291
|
+
begin
|
292
|
+
val = Integer(val)
|
293
|
+
rescue => e
|
294
|
+
if val.start_with?("'") and val.end_with?("'")
|
295
|
+
val = val[1..-2]
|
296
|
+
elsif val.start_with?('"') and val.end_with?('"')
|
297
|
+
val = val[1..-2]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
"#{::Vertica.quote_identifier(col)} = #{::Vertica.quote(val)}"
|
301
|
+
end.join(' AND ')
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
data/prepare.sh
ADDED
data/start.sh
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
ABSPATH=$(cd $(dirname $0) && pwd)/$(basename $0)
|
3
|
+
APP_ROOT=$(dirname $ABSPATH)
|
4
|
+
if [ -z "${SHARED_ROOT}" ]; then SHARED_ROOT=.; fi
|
5
|
+
|
6
|
+
CMD="bundle exec triglav-agent-vertica --dotenv -c config.yml --status ${SHARED_ROOT}/status.yml --token ${SHARED_ROOT}/token.yml"
|
7
|
+
echo $CMD
|
8
|
+
$CMD
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'triglav/agent/vertica/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "triglav-agent-vertica"
|
8
|
+
spec.version = Triglav::Agent::Vertica::VERSION
|
9
|
+
spec.authors = ["Triglav Team"]
|
10
|
+
spec.email = ["triglav_admin_my@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Vertica agent for Triglav, data-driven workflow tool.}
|
13
|
+
spec.description = %q{Vertica agent for Triglav, data-driven workflow tool.}
|
14
|
+
spec.homepage = "https://github.com/triglav-dataflow/triglav-agent-vertica"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "vertica"
|
23
|
+
spec.add_dependency "triglav-agent"
|
24
|
+
spec.add_dependency "triglav_client"
|
25
|
+
spec.add_dependency "rack" # Rack::Utils
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "test-unit"
|
30
|
+
spec.add_development_dependency "test-unit-rr"
|
31
|
+
spec.add_development_dependency "test-unit-power_assert"
|
32
|
+
spec.add_development_dependency "timecop"
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: triglav-agent-vertica
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.rc1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Triglav Team
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: vertica
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: triglav-agent
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: triglav_client
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.11'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.11'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: test-unit
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: test-unit-rr
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: test-unit-power_assert
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: timecop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: Vertica agent for Triglav, data-driven workflow tool.
|
154
|
+
email:
|
155
|
+
- triglav_admin_my@gmail.com
|
156
|
+
executables:
|
157
|
+
- triglav-agent-vertica
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- ".gitignore"
|
162
|
+
- ".rspec"
|
163
|
+
- ".travis.yml"
|
164
|
+
- CODE_OF_CONDUCT.md
|
165
|
+
- Gemfile
|
166
|
+
- LICENSE.txt
|
167
|
+
- README.md
|
168
|
+
- Rakefile
|
169
|
+
- bin/console
|
170
|
+
- bin/setup
|
171
|
+
- example/config.yml
|
172
|
+
- example/example.env
|
173
|
+
- exe/triglav-agent-vertica
|
174
|
+
- lib/triglav/agent/vertica.rb
|
175
|
+
- lib/triglav/agent/vertica/connection.rb
|
176
|
+
- lib/triglav/agent/vertica/monitor.rb
|
177
|
+
- lib/triglav/agent/vertica/version.rb
|
178
|
+
- prepare.sh
|
179
|
+
- start.sh
|
180
|
+
- triglav-agent-vertica.gemspec
|
181
|
+
homepage: https://github.com/triglav-dataflow/triglav-agent-vertica
|
182
|
+
licenses:
|
183
|
+
- MIT
|
184
|
+
metadata: {}
|
185
|
+
post_install_message:
|
186
|
+
rdoc_options: []
|
187
|
+
require_paths:
|
188
|
+
- lib
|
189
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - ">"
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: 1.3.1
|
199
|
+
requirements: []
|
200
|
+
rubyforge_project:
|
201
|
+
rubygems_version: 2.5.2
|
202
|
+
signing_key:
|
203
|
+
specification_version: 4
|
204
|
+
summary: Vertica agent for Triglav, data-driven workflow tool.
|
205
|
+
test_files: []
|