triglav-agent-vertica 1.0.0.rc1

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
+ SHA1:
3
+ metadata.gz: 60cfd7322991b2deda4453644f24760066d26bed
4
+ data.tar.gz: 28993b067403c1caeac88eda684f03b47b2736a4
5
+ SHA512:
6
+ metadata.gz: 666c7ed4db2c4b8f660131d049708b146939fab77d6713af173258c59da99b4393a6efa1c1bd28326418139244c813849dfed746ef3ec01e0303786813fea027
7
+ data.tar.gz: 1532c2898a58e4beaec1cb91ab1317f08b550e2a5de1aec7c04164e5894206b17dbd806587ece7c050d2bf46ac5d713008ee7a22b7da8899bead717a077024f3
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .env
11
+ /status.yml
12
+ /token.yml
13
+ /config.yml
14
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ - 2.4.0
5
+ before_install:
6
+ - gem install bundler -v 1.11.2
@@ -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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+ gem 'triglav_client', git: 'https://github.com/triglav-dataflow/triglav-client-ruby'
5
+ gem 'triglav-agent', git: 'https://github.com/triglav-dataflow/triglav-agent-framework-ruby'
6
+ gem 'pry-nav'
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&timestamp=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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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
@@ -0,0 +1,8 @@
1
+ TRIGLAV_URL=http://localhost:7800
2
+ TRIGLAV_USERNAME=triglav_test
3
+ TRIGLAV_PASSWORD=triglav_test
4
+ VERTICA_HOST=xxx.xxx.xxx.xxx
5
+ VERTICA_PORT=5433
6
+ VERTICA_DATABASE=vdb
7
+ VERTICA_USER=dbread
8
+ VERTICA_PASSWORD=daerbd
@@ -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,11 @@
1
+ module Triglav
2
+ module Agent
3
+ module Vertica
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'triglav-agent'
9
+ require 'triglav/agent/vertica/connection'
10
+ require 'triglav/agent/vertica/version'
11
+ require 'triglav/agent/vertica/monitor'
@@ -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
@@ -0,0 +1,7 @@
1
+ module Triglav
2
+ module Agent
3
+ module Vertica
4
+ VERSION = "1.0.0.rc1"
5
+ end
6
+ end
7
+ end
data/prepare.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+ test -f config.yml || cp example/config.yml config.yml
3
+ test -f .env || cp example/example.env .env
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: []