hotdog 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 953ecab33a4f48f426c3df8b0dcca49e861e5113
4
+ data.tar.gz: 81868bd92db6fddf876fd7eae71c54a2dd841bad
5
+ SHA512:
6
+ metadata.gz: 2763f5fa563fef343b799514436bde84dd886494577b42269118c4e82d999d1c8b3c289e50e3c2a02ba4fb8806c00fbd2a2f8abe4054abc90a9457684daad7d1
7
+ data.tar.gz: bc471b4a441f275d7fbbf6b2df8d706016de004c80a949fa86499db0d09ad174b708d5864248332d4c540d00731e146e6039f97d61c090422ac7125ea229504e
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.swo
13
+ *.swp
14
+ *.o
15
+ *.a
16
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org/"
2
+
3
+ # Specify your gem's dependencies in hotdog.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Yamashita Yuu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Hotdog
2
+
3
+ Yet another command-line tools for [Datadog](https://www.datadoghq.com/).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hotdog'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```sh
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```
22
+ $ gem install hotdog
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ Setup environment variables of `DATADOG_API_KEY` and `DATADOG_APPLICATION_KEY`.
28
+ Then, create and initialize host information. This may take several minutes.
29
+
30
+ ```sh
31
+ $ hotdog init
32
+ $ hotdot update
33
+ ```
34
+
35
+ List all registered hosts.
36
+
37
+ ```sh
38
+ $ hotdog ls
39
+ i-02605a79
40
+ i-02d78cec
41
+ i-03cb56ed
42
+ i-03dabcef
43
+ i-069e282c
44
+ ```
45
+
46
+ List all registered hosts with associated tags and headers.
47
+
48
+ ```sh
49
+ $ hotdog ls -h -l
50
+ host security-group name availability-zone instance-type image region kernel
51
+ ---------- -------------- ----------------- ----------------- ------------- ------------ --------- ------------
52
+ i-02605a79 sg-89bfe710 web-staging us-east-1a m3.medium ami-66089cdf us-east-1 aki-89ab75e1
53
+ i-02d78cec sg-89bfe710 web-production us-east-1a c3.4xlarge ami-8bb3fc92 us-east-1 aki-89ab75e1
54
+ i-03cb56ed sg-89bfe710 web-production us-east-1b c3.4xlarge ami-8bb3fc92 us-east-1 aki-89ab75e1
55
+ i-03dabcef sg-89bfe710 worker-production us-east-1a c3.xlarge ami-4032c1c8 us-east-1 aki-89ab75e1
56
+ i-069e282c sg-89bfe710 worker-staging us-east-1a t2.micro ami-384c8480 us-east-1 aki-89ab75e1
57
+ ```
58
+
59
+ Display hosts with specific attributes.
60
+
61
+ ```sh
62
+ $ hotdog ls -a host -a name
63
+ host name
64
+ ---------- -----------------
65
+ i-02605a79 web-staging
66
+ i-02d78cec web-production
67
+ i-03cb56ed web-production
68
+ i-03dabcef worker-production
69
+ i-069e282c worker-staging
70
+ ```
71
+
72
+ Search hosts matching to specified tags and values.
73
+
74
+ ```sh
75
+ $ hotdog search availability-zone:us-east-1b and 'name:web-*'
76
+ i-03cb56ed
77
+ ```
78
+
79
+ ## Contributing
80
+
81
+ 1. Fork it ( https://github.com/yyuu/hotdog/fork )
82
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
83
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
84
+ 4. Push to the branch (`git push origin my-new-feature`)
85
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/hotdog ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
3
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
4
+ require "bundler/setup"
5
+ require "hotdog/application"
6
+ dog = Hotdog::Application.new
7
+ dog.main(ARGV.dup)
data/hotdog.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "hotdog/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hotdog"
8
+ spec.version = Hotdog::VERSION
9
+ spec.authors = ["Yamashita Yuu"]
10
+ spec.email = ["peek824545201@gmail.com"]
11
+ spec.summary = %q{Yet another command-line tool for Datadog}
12
+ spec.description = %q{Yet another command-line tool for Datadog}
13
+ spec.homepage = "https://github.com/yyuu/hotdog"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_dependency "dogapi", "~> 1.13.0"
25
+ spec.add_dependency "parslet", "~> 1.6.2"
26
+ spec.add_dependency "sqlite3", "~> 1.3.10"
27
+ end
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "logger"
4
+ require "optparse"
5
+ require "shellwords"
6
+ require "sqlite3"
7
+ require "hotdog/commands"
8
+ require "hotdog/formatters"
9
+
10
+ module Hotdog
11
+ class Application
12
+ def initialize()
13
+ @confdir = File.join(ENV["HOME"], ".hotdog")
14
+ @optparse = OptionParser.new
15
+ @options = {
16
+ environment: "default",
17
+ minimum_expiry: 28800, # 8 hours
18
+ random_expiry: 57600, # 16 hours
19
+ force: false,
20
+ formatter: get_formatter("plain").new,
21
+ headers: false,
22
+ listing: false,
23
+ logger: Logger.new(STDERR),
24
+ api_key: ENV["DATADOG_API_KEY"],
25
+ application_key: ENV["DATADOG_APPLICATION_KEY"],
26
+ print0: false,
27
+ print1: true,
28
+ tags: [],
29
+ }
30
+ @options[:logger].level = Logger::INFO
31
+ define_options
32
+ end
33
+ attr_reader :options
34
+
35
+ def main(argv=[])
36
+ config = File.join(@confdir, "config.yml")
37
+ if File.file?(config)
38
+ @options = @options.merge(YAML.load(File.read(config)))
39
+ end
40
+ args = @optparse.parse(argv)
41
+
42
+ unless options[:api_key]
43
+ raise("DATADOG_API_KEY is not set")
44
+ end
45
+
46
+ unless options[:application_key]
47
+ raise("DATADOG_APPLICATION_KEY is not set")
48
+ end
49
+
50
+ sqlite = File.expand_path(File.join(@confdir, "#{options[:environment]}.db"))
51
+ FileUtils.mkdir_p(File.dirname(sqlite))
52
+ @db = SQLite3::Database.new(sqlite)
53
+ @db.synchronous = "off"
54
+
55
+ begin
56
+ command = ( args.shift || "help" )
57
+ run_command(command, args)
58
+ rescue Errno::EPIPE
59
+ # nop
60
+ end
61
+ end
62
+
63
+ def run_command(command, args=[])
64
+ get_command(command).new(@db, options.merge(application: self)).run(args)
65
+ end
66
+
67
+ private
68
+ def define_options
69
+ @optparse.on("--api-key API_KEY") do |api_key|
70
+ options[:api_key] = api_key
71
+ end
72
+ @optparse.on("--application-key APP_KEY") do |app_key|
73
+ options[:application_key] = app_key
74
+ end
75
+ @optparse.on("-0", "--null") do
76
+ options[:print0] = true
77
+ end
78
+ @optparse.on("-1") do
79
+ options[:print1] = true
80
+ end
81
+ @optparse.on("-d", "--debug") do
82
+ options[:logger].level = Logger::DEBUG
83
+ end
84
+ @optparse.on("-E ENVIRONMENT", "--environment ENVIRONMENT") do |environment|
85
+ options[:environment] = environment
86
+ end
87
+ @optparse.on("-f", "--force") do
88
+ options[:force] = true
89
+ end
90
+ @optparse.on("-F FORMAT", "--format FORMAT") do |format|
91
+ options[:formatter] = get_formatter(format).new
92
+ end
93
+ @optparse.on("-h", "--headers") do |headers|
94
+ options[:headers] = headers
95
+ end
96
+ @optparse.on("-l") do
97
+ options[:listing] = true
98
+ end
99
+ @optparse.on("-a TAG", "-t TAG", "--tag TAG") do |tag|
100
+ options[:tags] += [tag]
101
+ end
102
+ @optparse.on("-V", "--verbose") do |tag|
103
+ options[:logger].level = Logger::DEBUG
104
+ end
105
+ end
106
+
107
+ def const_name(name)
108
+ name.to_s.split(/[^\w]+/).map { |s| s.capitalize }.join
109
+ end
110
+
111
+ def get_formatter(name)
112
+ begin
113
+ Hotdog::Formatters.const_get(const_name(name))
114
+ rescue NameError
115
+ if library = find_library("hotdog/formatters", name)
116
+ load library
117
+ Hotdog::Formatters.const_get(const_name(File.basename(library, ".rb")))
118
+ else
119
+ raise(NameError.new("unknown format: #{name}"))
120
+ end
121
+ end
122
+ end
123
+
124
+ def get_command(name)
125
+ begin
126
+ Hotdog::Commands.const_get(const_name(name))
127
+ rescue NameError
128
+ if library = find_library("hotdog/commands", name)
129
+ load library
130
+ Hotdog::Commands.const_get(const_name(File.basename(library, ".rb")))
131
+ else
132
+ raise(NameError.new("unknown command: #{name}"))
133
+ end
134
+ end
135
+ end
136
+
137
+ def find_library(dirname, name)
138
+ load_path = $LOAD_PATH.map { |path| File.join(path, dirname) }.select { |path| File.directory?(path) }
139
+ libraries = load_path.map { |path| Dir.glob(File.join(path, "*.rb")) }.reduce(:+).select { |file| File.file?(file) }
140
+ rbname = "#{name}.rb"
141
+ if library = libraries.find { |file| File.basename(file) == rbname }
142
+ library
143
+ else
144
+ candidates = libraries.map { |file| [file, File.basename(file).slice(0, name.length)] }.select { |file, s| s == name }
145
+ if candidates.length == 1
146
+ candidates.first.first
147
+ else
148
+ nil
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ # vim:set ft=ruby :
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Destroy < BaseCommand
6
+ def run(args=[])
7
+ execute(<<-EOS)
8
+ DROP TABLE IF EXISTS hosts;
9
+ EOS
10
+ execute(<<-EOS)
11
+ DROP TABLE IF EXISTS tags;
12
+ EOS
13
+ execute(<<-EOS)
14
+ DROP TABLE IF EXISTS hosts_tags;
15
+ EOS
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # vim:set ft=ruby :
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Gc < BaseCommand
6
+ def run(args=[])
7
+ execute(<<-EOS)
8
+ DELETE FROM hosts WHERE id NOT IN ( SELECT DISTINCT host_id FROM hosts_tags );
9
+ EOS
10
+ execute(<<-EOS)
11
+ DELETE FROM tags WHERE id NOT IN ( SELECT DISTINCT tag_id FROM hosts_tags );
12
+ EOS
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ # vim:set ft=ruby :
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rbconfig"
4
+
5
+ module Hotdog
6
+ module Commands
7
+ class Help < BaseCommand
8
+ def run(args=[])
9
+ ruby = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
10
+ exit(system(ruby, $0, "--help") ? 0 : 1)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ # vim:set ft=ruby :
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Hosts < BaseCommand
6
+ def run(args=[])
7
+ update_hosts(@options.dup)
8
+
9
+ if args.empty?
10
+ result = execute(<<-EOS).map { |row| row.first }
11
+ SELECT DISTINCT host_id FROM hosts_tags;
12
+ EOS
13
+ else
14
+ result = args.map { |host_name|
15
+ if host_name.index("*")
16
+ execute(<<-EOS, host_name).map { |row| row.first }
17
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
18
+ INNER JOIN hosts ON hosts_tags.host_id = hosts.id
19
+ WHERE LOWER(hosts.name) GLOB LOWER(?);
20
+ EOS
21
+ else
22
+ execute(<<-EOS, host_name).map { |row| row.first }
23
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
24
+ INNER JOIN hosts ON hosts_tags.host_id = hosts.id
25
+ WHERE LOWER(hosts.name) = LOWER(?);
26
+ EOS
27
+ end
28
+ }.reduce(:+)
29
+ end
30
+ if 0 < result.length
31
+ result, fields = get_hosts(result)
32
+ STDOUT.puts(format(result, fields: fields))
33
+ else
34
+ STDERR.puts("no match found: #{args.join(" ")}")
35
+ exit(1)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # vim:set ft=ruby :
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Init < BaseCommand
6
+ def run(args=[])
7
+ execute(<<-EOS)
8
+ CREATE TABLE IF NOT EXISTS hosts (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ name VARCHAR(255) NOT NULL
11
+ );
12
+ EOS
13
+ execute(<<-EOS)
14
+ CREATE UNIQUE INDEX IF NOT EXISTS hosts_name ON hosts ( name );
15
+ EOS
16
+ execute(<<-EOS)
17
+ CREATE TABLE IF NOT EXISTS tags (
18
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19
+ name VARCHAR(200) NOT NULL,
20
+ value VARCHAR(200) NOT NULL DEFAULT ""
21
+ );
22
+ EOS
23
+ execute(<<-EOS)
24
+ CREATE UNIQUE INDEX IF NOT EXISTS tags_name_value ON tags ( name, value );
25
+ EOS
26
+ execute(<<-EOS)
27
+ CREATE TABLE IF NOT EXISTS hosts_tags (
28
+ host_id INTEGER NOT NULL,
29
+ tag_id INTEGER NOT NULL,
30
+ expires_at INTEGER NOT NULL
31
+ );
32
+ EOS
33
+ execute(<<-EOS)
34
+ CREATE UNIQUE INDEX IF NOT EXISTS hosts_tags_host_id_tag_id ON hosts_tags ( host_id, tag_id );
35
+ EOS
36
+ execute(<<-EOS)
37
+ CREATE INDEX IF NOT EXISTS hosts_tags_expires_at ON hosts_tags ( expires_at );
38
+ EOS
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # vim:set ft=ruby :
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "hotdog/commands/hosts"
4
+
5
+ module Hotdog
6
+ module Commands
7
+ class Ls < Hosts
8
+ end
9
+ end
10
+ end
11
+
12
+ # vim:set ft=ruby :
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Rm < BaseCommand
6
+ def run(args=[])
7
+ execute(<<-EOS % args.map { "?" }.join(", "), args).map { |row| row.first }
8
+ DELETE FROM hosts_tags
9
+ WHERE host_id IN
10
+ ( SELECT hosts_tags.host_id FROM hosts_tags
11
+ INNER JOIN hosts ON hosts_tags.host_id = hosts.id
12
+ WHERE hosts.name NOT IN (%s) );
13
+ EOS
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # vim:set ft=ruby :
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "json"
4
+ require "parslet"
5
+
6
+ module Hotdog
7
+ module Commands
8
+ class Search < BaseCommand
9
+ def run(args=[])
10
+ expression = args.join(" ").strip
11
+ if expression.empty?
12
+ exit(1)
13
+ end
14
+
15
+ update_hosts(@options.dup)
16
+ # update_tags(@options.dup)
17
+
18
+ begin
19
+ node = parse(expression)
20
+ rescue Parslet::ParseFailed => error
21
+ STDERR.puts("syntax error: " + error.cause.ascii_tree)
22
+ exit(1)
23
+ end
24
+ result = evaluate(node, self).sort
25
+ if 0 < result.length
26
+ result, fields = get_hosts(result)
27
+ STDOUT.puts(format(result, fields: fields))
28
+ else
29
+ STDERR.puts("no match found: #{args.join(" ")}")
30
+ exit(1)
31
+ end
32
+ end
33
+
34
+ def parse(expression)
35
+ parser = ExpressionParser.new
36
+ parser.parse(expression).tap do |parsed|
37
+ logger.debug(JSON.pretty_generate(JSON.load(parsed.to_json)))
38
+ end
39
+ end
40
+
41
+ def evaluate(node, environment)
42
+ node = ExpressionTransformer.new.apply(node)
43
+ node.evaluate(environment)
44
+ end
45
+
46
+ class ExpressionParser < Parslet::Parser
47
+ root(:expression)
48
+ rule(:expression) {
49
+ ( binary_expression \
50
+ | term \
51
+ )
52
+ }
53
+ rule(:binary_expression) {
54
+ ( term.as(:left) >> spacing.maybe >> (str('&') >> str('&').maybe).as(:binary_op) >> spacing.maybe >> expression.as(:right) \
55
+ | term.as(:left) >> spacing.maybe >> (str('|') >> str('|').maybe).as(:binary_op) >> spacing.maybe >> expression.as(:right) \
56
+ | term.as(:left) >> spacing.maybe >> (match('[Aa]') >> match('[Nn]') >> match('[Dd]')).as(:binary_op) >> spacing.maybe >> expression.as(:right) \
57
+ | term.as(:left) >> spacing.maybe >> (match('[Oo]') >> match('[Rr]')).as(:binary_op) >> spacing.maybe >> expression.as(:right) \
58
+ )
59
+ }
60
+ rule(:unary_expression) {
61
+ ( spacing.maybe >> str('!').as(:unary_op) >> atom.as(:expression) \
62
+ | spacing.maybe >> str('~').as(:unary_op) >> atom.as(:expression) \
63
+ | spacing.maybe >> str('not').as(:unary_op) >> atom.as(:expression) \
64
+ )
65
+ }
66
+ rule(:term) {
67
+ ( unary_expression \
68
+ | atom \
69
+ )
70
+ }
71
+ rule(:atom) {
72
+ ( spacing.maybe >> str('(') >> expression >> str(')') >> spacing.maybe \
73
+ | spacing.maybe >> identifier_regexp.as(:identifier_regexp) >> str(':') >> attribute_regexp.as(:attribute_regexp) >> spacing.maybe \
74
+ | spacing.maybe >> identifier_regexp.as(:identifier_regexp) >> spacing.maybe \
75
+ | spacing.maybe >> identifier_glob.as(:identifier_glob) >> str(':') >> attribute_glob.as(:attribute_glob) >> spacing.maybe \
76
+ | spacing.maybe >> identifier_glob.as(:identifier_glob) >> str(':') >> attribute.as(:attribute) >> spacing.maybe \
77
+ | spacing.maybe >> identifier_glob.as(:identifier_glob) >> spacing.maybe \
78
+ | spacing.maybe >> identifier.as(:identifier)>> str(':') >> attribute_glob.as(:attribute_glob) >> spacing.maybe \
79
+ | spacing.maybe >> identifier.as(:identifier)>> str(':') >> attribute.as(:attribute) >> spacing.maybe \
80
+ | spacing.maybe >> identifier.as(:identifier) >> spacing.maybe \
81
+ )
82
+ }
83
+ rule(:identifier_regexp) {
84
+ ( str('/') >> (str('/').absent? >> any).repeat(0) >> str('/') \
85
+ )
86
+ }
87
+ rule(:identifier_glob) {
88
+ ( identifier.repeat(0) >> (glob >> identifier.maybe).repeat(1) \
89
+ )
90
+ }
91
+ rule(:identifier) {
92
+ ( match('[A-Za-z]') >> match('[-./0-9A-Z_a-z]').repeat(0) \
93
+ )
94
+ }
95
+ rule(:attribute_regexp) {
96
+ ( str('/') >> (str('/').absent? >> any).repeat(0) >> str('/') \
97
+ )
98
+ }
99
+ rule(:attribute_glob) {
100
+ ( attribute.repeat(0) >> (glob >> attribute.maybe).repeat(1) \
101
+ )
102
+ }
103
+ rule(:attribute) {
104
+ ( match('[-./0-9:A-Z_a-z]').repeat(1) \
105
+ )
106
+ }
107
+ rule(:glob) {
108
+ ( str('*') | str('?') | str('[') | str(']') )
109
+ }
110
+ rule(:spacing) {
111
+ ( match('[\t\n\r ]').repeat(1) \
112
+ )
113
+ }
114
+ end
115
+
116
+ class ExpressionTransformer < Parslet::Transform
117
+ rule(:binary_op => simple(:binary_op), :left => simple(:left), :right => simple(:right)) {
118
+ BinaryExpressionNode.new(binary_op, left, right)
119
+ }
120
+ rule(:unary_op => simple(:unary_op), :expression => simple(:expression)) {
121
+ UnaryExpressionNode.new(unary_op, expression)
122
+ }
123
+ rule(:identifier_regexp => simple(:identifier_regexp), :attribute_regexp => simple(:attribute_regexp)) {
124
+ TagRegexpExpressionNode.new(identifier_regexp.to_s, attribute_regexp.to_s)
125
+ }
126
+ rule(:identifier_regexp => simple(:identifier_regexp)) {
127
+ TagRegexpExpressionNode.new(identifier_regexp.to_s, nil)
128
+ }
129
+ rule(:identifier_glob => simple(:identifier_glob), :attribute_glob => simple(:attribute_glob)) {
130
+ TagGlobExpressionNode.new(identifier_glob.to_s, attribute_glob.to_s)
131
+ }
132
+ rule(:identifier_glob => simple(:identifier_glob), :attribute => simple(:attribute)) {
133
+ TagGlobExpressionNode.new(identifier_glob.to_s, attribute.to_s)
134
+ }
135
+ rule(:identifier_glob => simple(:identifier_glob)) {
136
+ TagGlobExpressionNode.new(identifier_glob.to_s, nil)
137
+ }
138
+ rule(:identifier => simple(:identifier), :attribute_glob => simple(:attribute_glob)) {
139
+ TagGlobExpressionNode.new(identifier.to_s, attribute_glob.to_s)
140
+ }
141
+ rule(:identifier => simple(:identifier), :attribute => simple(:attribute)) {
142
+ TagExpressionNode.new(identifier.to_s, attribute.to_s)
143
+ }
144
+ rule(:identifier => simple(:identifier)) {
145
+ TagExpressionNode.new(identifier.to_s, nil)
146
+ }
147
+ end
148
+
149
+ class ExpressionNode
150
+ def evaluate(environment)
151
+ raise(NotImplementedError)
152
+ end
153
+ end
154
+
155
+ class BinaryExpressionNode < ExpressionNode
156
+ def initialize(op, left, right)
157
+ @op = op
158
+ @left = left
159
+ @right = right
160
+ end
161
+ def evaluate(environment)
162
+ case @op
163
+ when "&&", "&", /\Aand\z/i
164
+ left_values = @left.evaluate(environment)
165
+ if left_values.empty?
166
+ []
167
+ else
168
+ (left_values & @right.evaluate(environment)).uniq
169
+ end
170
+ when "||", "|", /\Aor\z/i
171
+ left_values = @left.evaluate(environment)
172
+ (left_values | @right.evaluate(environment)).uniq
173
+ else
174
+ raise(SyntaxError.new("unknown binary operator: #{@op}"))
175
+ end
176
+ end
177
+ end
178
+
179
+ class UnaryExpressionNode < ExpressionNode
180
+ def initialize(op, expression)
181
+ @op = op
182
+ @expression = expression
183
+ end
184
+ def evaluate(environment)
185
+ case @op
186
+ when "!", "~", /\Anot\z/i
187
+ values = @expression.evaluate(environment)
188
+ if values.empty?
189
+ environment.execute(<<-EOS).map { |row| row.first }
190
+ SELECT DISTINCT host_id FROM hosts_tags;
191
+ EOS
192
+ else
193
+ environment.execute(<<-EOS % values.map { "?" }.join(", "), values).map { |row| row.first }
194
+ SELECT DISTINCT host_id FROM hosts_tags WHERE host_id NOT IN (%s);
195
+ EOS
196
+ end
197
+ else
198
+ raise(SyntaxError.new("unknown unary operator: #{@op}"))
199
+ end
200
+ end
201
+ end
202
+
203
+ class TagExpressionNode < ExpressionNode
204
+ def initialize(identifier, attribute)
205
+ @identifier = identifier
206
+ @attribute = attribute
207
+ end
208
+ attr_reader :identifier
209
+ attr_reader :attribute
210
+ def attribute?
211
+ !attribute.nil?
212
+ end
213
+ def evaluate(environment)
214
+ if attribute?
215
+ environment.execute(<<-EOS, identifier, attribute).map { |row| row.first }
216
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
217
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
218
+ WHERE LOWER(tags.name) = LOWER(?) AND LOWER(tags.value) = LOWER(?);
219
+ EOS
220
+ else
221
+ environment.execute(<<-EOS, identifier, identifier, identifier).map { |row| row.first }
222
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
223
+ INNER JOIN hosts ON hosts_tags.host_id = hosts.id
224
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
225
+ WHERE LOWER(hosts.name) = LOWER(?) OR LOWER(tags.name) = LOWER(?) OR LOWER(tags.value) = LOWER(?);
226
+ EOS
227
+ end
228
+ end
229
+ end
230
+
231
+ class TagGlobExpressionNode < TagExpressionNode
232
+ def evaluate(environment)
233
+ if attribute?
234
+ environment.execute(<<-EOS, identifier, attribute).map { |row| row.first }
235
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
236
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
237
+ WHERE LOWER(tags.name) GLOB LOWER(?) AND LOWER(tags.value) GLOB LOWER(?);
238
+ EOS
239
+ else
240
+ environment.execute(<<-EOS, identifier, identifier, identifier).map { |row| row.first }
241
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
242
+ INNER JOIN hosts ON hosts_tags.host_id = hosts.id
243
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
244
+ WHERE LOWER(hosts.name) GLOB LOWER(?) OR LOWER(tags.name) GLOB LOWER(?) OR LOWER(tags.value) GLOB LOWER(?);
245
+
246
+ EOS
247
+ end
248
+ end
249
+ end
250
+
251
+ class TagRegexpExpressionNode < TagExpressionNode
252
+ def initialize(identifier, attribute)
253
+ identifier = identifier.sub(%r{\A/(.*)/\z}) { $1 } if identifier
254
+ attribute = attribute.sub(%r{\A/(.*)/\z}) { $1 } if attribute
255
+ super(identifier, attribute)
256
+ end
257
+ def evaluate(environment)
258
+ if attribute?
259
+ environment.execute(<<-EOS, identifier, attribute).map { |row| row.first }
260
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
261
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
262
+ WHERE LOWER(tags.name) REGEXP LOWER(?) AND LOWER(tags.value) REGEXP LOWER(?);
263
+ EOS
264
+ else
265
+ environment.execute(<<-EOS, identifier, identifier, identifier).map { |row| row.first }
266
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
267
+ INNER JOIN hosts ON hosts_tags.host_id = hosts.id
268
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
269
+ WHERE LOWER(hosts.name) REGEXP LOWER(?) OR LOWER(tags.name) REGEXP LOWER(?) OR LOWER(tags.value) REGEXP LOWER(?);
270
+ EOS
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ # vim:set ft=ruby :
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Tags < BaseCommand
6
+ def run(args=[])
7
+ update_tags(@options.dup)
8
+ if 0 < tags.length
9
+ fields = tags.map { |tag|
10
+ tag_name, tag_value = tag.split(":", 2)
11
+ tag_name
12
+ }
13
+ result1 = fields.map { |tag_name|
14
+ execute(<<-EOS, tag_name).map { |row| row.join(",") }
15
+ SELECT DISTINCT tags.value FROM hosts_tags
16
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
17
+ WHERE tags.name = LOWER(?);
18
+ EOS
19
+ }
20
+ result = (0..result1.reduce(0) { |max, values| [max, values.length].max }).map { |field_index|
21
+ result1.map { |values| values[field_index] }
22
+ }
23
+ else
24
+ fields = ["tag"]
25
+ result = execute(<<-EOS).map { |name, value| [0 < value.length ? "#{name}:#{value}" : name] }
26
+ SELECT tags.name, tags.value FROM hosts_tags
27
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id;
28
+ EOS
29
+ end
30
+ if 0 < result.length
31
+ STDOUT.puts(format(result, fields: fields))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ # vim:set ft=ruby :
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Commands
5
+ class Update < BaseCommand
6
+ def run(args=[])
7
+ application.run_command("init")
8
+ if 0 < args.length
9
+ args.each do |host_name|
10
+ update_host_tags(host_name, @options.dup)
11
+ end
12
+ else
13
+ update_tags(@options.dup)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # vim:set ft=ruby :
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "dogapi"
4
+ require "logger"
5
+
6
+ module Hotdog
7
+ module Commands
8
+ class BaseCommand
9
+ def initialize(db, options={})
10
+ @db = db
11
+ @formatter = options[:formatter]
12
+ @logger = options[:logger]
13
+ @tags = options[:tags]
14
+ @application = options[:application]
15
+ @options = options
16
+ @dog = Dogapi::Client.new(options[:api_key], options[:application_key])
17
+ end
18
+ attr_reader :application
19
+ attr_reader :formatter
20
+ attr_reader :logger
21
+ attr_reader :tags
22
+ attr_reader :options
23
+
24
+ def run(args=[])
25
+ raise(NotImplementedError)
26
+ end
27
+
28
+ def execute(query, *args)
29
+ q = query.strip
30
+ if 0 < args.length
31
+ q += " -- VALUES (#{args.map { |arg| Array === arg ? "(#{arg.join(", ")})" : arg.inspect }.join(", ")})"
32
+ end
33
+ logger.debug(q)
34
+ @db.execute(query, args)
35
+ end
36
+
37
+ private
38
+ def format(result, options={})
39
+ @formatter.format(result, @options.merge(options))
40
+ end
41
+
42
+ def get_hosts(hosts=[])
43
+ if 0 < tags.length
44
+ result = hosts.map { |host_id|
45
+ update_host_tags(host_id, @options.merge(tags: tags))
46
+ tags.map { |tag|
47
+ tag_name, tag_value = tag.split(":", 2)
48
+ if tag_name == "host"
49
+ logger.debug("get_hosts_q1()")
50
+ @get_hosts_q1 ||= @db.prepare(<<-EOS)
51
+ SELECT name FROM hosts WHERE id = ? LIMIT 1;
52
+ EOS
53
+ @get_hosts_q1.execute(host_id).map { |row| row.first }.join(",")
54
+ else
55
+ logger.debug("get_hosts_q2()")
56
+ @get_hosts_q2 ||= @db.prepare(<<-EOS)
57
+ SELECT tags.value FROM hosts_tags
58
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
59
+ WHERE hosts_tags.host_id = ? AND tags.name = ?;
60
+ EOS
61
+ @get_hosts_q2.execute(host_id, tag_name).map { |row| row.first }.join(",")
62
+ end
63
+ }
64
+ }
65
+ fields = tags
66
+ else
67
+ if options[:listing]
68
+
69
+ fields = []
70
+ hosts = execute(<<-EOS % hosts.map { "?" }.join(", "), hosts)
71
+ SELECT id, name FROM hosts WHERE id IN (%s) ORDER BY name;
72
+ EOS
73
+ result = hosts.map { |host_id, host_name|
74
+ update_host_tags(host_name, @options.dup)
75
+ logger.debug("get_hosts_q3()")
76
+ @get_hosts_q3 ||= @db.prepare(<<-EOS)
77
+ SELECT DISTINCT tags.name FROM hosts_tags
78
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
79
+ WHERE hosts_tags.host_id = ?;
80
+ EOS
81
+ tag_names = @get_hosts_q3.execute(host_id).map { |row| row.first }
82
+ tag_names.each do |tag_name|
83
+ fields << tag_name unless fields.index(tag_name)
84
+ end
85
+ [host_name] + fields.map { |tag_name|
86
+ @get_hosts_q4 ||= @db.prepare(<<-EOS)
87
+ SELECT tags.value FROM hosts_tags
88
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
89
+ WHERE hosts_tags.host_id = ? AND tags.name = ?;
90
+ EOS
91
+ logger.debug("get_hosts_q4(%s, %s)" % [host_id.inspect, tag_name.inspect])
92
+ @get_hosts_q4.execute(host_id, tag_name).map { |row| row.first }.join(",")
93
+ }
94
+ }
95
+ fields = ["host"] + fields
96
+ else
97
+ fields = ["host"]
98
+ result = execute(<<-EOS % hosts.map { "?" }.join(", "), hosts)
99
+ SELECT name FROM hosts WHERE id IN (%s) ORDER BY name;
100
+ EOS
101
+ end
102
+ end
103
+ [result, fields]
104
+ end
105
+
106
+ def update_hosts(options={})
107
+ @db.transaction do
108
+ if not options[:force]
109
+ @update_hosts_q1 ||= @db.prepare("SELECT MIN(expires_at) FROM hosts_tags;")
110
+ logger.debug("update_hosts_q1()")
111
+ if expires_at = @update_hosts_q1.execute().map { |row| row.first }.first
112
+ if Time.new.to_i < expires_at
113
+ return
114
+ else
115
+ logger.debug("minimum expires_at was %s. start updateing." % [Time.at(expires_at)])
116
+ end
117
+ else
118
+ logger.debug("expires_at not found. start updateing.")
119
+ end
120
+ end
121
+
122
+ code, result = @dog.search("hosts:")
123
+ if code.to_i / 100 != 2
124
+ raise("HTTP #{code}: #{result.inspect}")
125
+ end
126
+
127
+ result["results"]["hosts"].each do |host_name|
128
+ @update_hosts_q2 ||= @db.prepare("INSERT OR IGNORE INTO hosts (name) VALUES (?);")
129
+ logger.debug("update_hosts_q2(%s)" % [host_name.inspect])
130
+ @update_hosts_q2.execute("INSERT OR IGNORE INTO hosts (name) VALUES (?);", host_name)
131
+ update_host_tags(host_name, options)
132
+ end
133
+
134
+ execute(<<-EOS % result["results"]["hosts"].map { "LOWER(?)" }.join(", "), result["results"]["hosts"])
135
+ DELETE FROM hosts_tags WHERE host_id NOT IN
136
+ ( SELECT id FROM hosts WHERE name IN ( %s ) );
137
+ EOS
138
+ end
139
+ end
140
+
141
+ def update_tags(options={})
142
+ @db.transaction do
143
+ if options[:force]
144
+ @update_tags_q1 ||= @db.prepare(<<-EOS)
145
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags;
146
+ EOS
147
+ logger.debug("update_tags_q1()")
148
+ hosts = @update_tags_q1.execute().map { |row| row.first }
149
+ else
150
+ logger.debug("update_tags_q2()")
151
+ @update_tags_q2 ||= @db.prepare(<<-EOS)
152
+ SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
153
+ WHERE hosts_tags.expires_at < ?;
154
+ EOS
155
+ hosts = @update_tags_q2.execute(Time.new.to_i)
156
+ end
157
+ hosts.each do |host_name|
158
+ update_host_tags(host_name, options)
159
+ end
160
+ end
161
+ end
162
+
163
+ def update_host_tags(host_name, options={})
164
+ if Integer === host_name
165
+ host_id = host_name
166
+ @update_host_tags_q1 ||= @db.prepare("SELECT name FROM hosts WHERE id = ? LIMIT 1;")
167
+ logger.debug("update_host_tags_q1(%s)" % [host_id.inspect])
168
+ host_name = @update_host_tags_q1.execute(host_id).map { |row| row.first }.first
169
+ else
170
+ @update_host_tags_q2 ||= @db.prepare("SELECT id FROM hosts WHERE LOWER(name) = LOWER(?) LIMIT 1;")
171
+ logger.debug("update_host_tags_q2(%s)" % [host_name.inspect])
172
+ host_id = @update_host_tags_q2.execute(host_name).map { |row| row.first }.first
173
+ end
174
+
175
+ if not options[:force]
176
+ @update_host_tags_q3 ||= @db.prepare("SELECT MIN(expires_at) FROM hosts_tags WHERE host_id = ?;")
177
+ logger.debug("update_host_tags_q3(%s)" % [host_id.inspect])
178
+ if expires_at = @update_host_tags_q3.execute(host_id).map { |row| row.first }.first
179
+ if Time.new.to_i < expires_at
180
+ return
181
+ else
182
+ logger.debug("%s: minimum expires_at was %s. start updating." % [host_name, Time.at(expires_at)])
183
+ end
184
+ else
185
+ logger.debug("%s: expires_at not found. start updateing." % [host_name])
186
+ end
187
+ end
188
+
189
+ code, result = @dog.host_tags(host_name)
190
+ if code.to_i / 100 != 2
191
+ raise("HTTP #{code}: #{result.inspect}")
192
+ end
193
+
194
+ expires_at = Time.new.to_i + (options[:minimum_expiry] + rand(options[:random_expiry]))
195
+ logger.debug("%s: expires_at=%s" % [host_name, Time.at(expires_at)])
196
+
197
+ result["tags"].each do |tag|
198
+ tag_name, tag_value = tag.split(":", 2)
199
+ tag_value ||= ""
200
+
201
+ if options.has_key?(:tags) and not options[:tags].empty? and not options[:tags].index(tag_name)
202
+ next
203
+ else
204
+ @update_host_tags_q4 ||= @db.prepare("INSERT OR IGNORE INTO tags (name, value) VALUES (?, ?);")
205
+ logger.debug("update_host_tags_q4(%s, %s)" % [tag_name.inspect, tag_value.inspect])
206
+ @update_host_tags_q4.execute(tag_name, tag_value)
207
+ @update_host_tags_q5 ||= @db.prepare(<<-EOS)
208
+ INSERT OR REPLACE INTO hosts_tags (host_id, tag_id, expires_at)
209
+ SELECT host.id, tag.id, ? FROM
210
+ ( SELECT id FROM hosts WHERE name = ? ) AS host,
211
+ ( SELECT id FROM tags WHERE name = ? AND value = ? ) AS tag;
212
+ EOS
213
+ logger.debug("update_host_tags_q5(%s, %s)" % [expires_at, host_name, tag_name, tag_value])
214
+ @update_host_tags_q5.execute(expires_at, host_name, tag_name, tag_value)
215
+ end
216
+ end
217
+
218
+ @update_host_tags_q6 ||= @db.prepare(<<-EOS)
219
+ DELETE FROM hosts_tags WHERE host_id = ? and expires_at <= ?;
220
+ EOS
221
+ logger.debug("update_host_tags_q6(%s, %s)" % [host_id.inspect, Time.new.to_i.inspect])
222
+ @update_host_tags_q6.execute(host_id, Time.new.to_i)
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ # vim:set ft=ruby :
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "json"
4
+
5
+ module Hotdog
6
+ module Formatters
7
+ class Json < BaseFormatter
8
+ def format(result, options={})
9
+ JSON.pretty_generate(result)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ # vim:set ft=ruby :
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Formatters
5
+ class Plain < BaseFormatter
6
+ def format(result, options={})
7
+ if options[:print0]
8
+ sep = "\0"
9
+ elsif options[:print1]
10
+ sep = "\n"
11
+ else
12
+ sep = " "
13
+ end
14
+ if options[:print1] and options[:headers] and options[:fields]
15
+ field_length = (0...result.last.length).map { |field_index|
16
+ result.reduce(0) { |length, row|
17
+ [length, row[field_index].to_s.length, options[:fields][field_index].to_s.length].max
18
+ }
19
+ }
20
+ header_fields = options[:fields].zip(field_length).map { |field, length|
21
+ field.to_s + (" " * (length - field.length))
22
+ }
23
+ result = [
24
+ header_fields,
25
+ header_fields.map { |field|
26
+ "-" * field.length
27
+ },
28
+ ] + result.map { |row|
29
+ row.zip(field_length).map { |field, length|
30
+ field.to_s + (" " * (length - field.length))
31
+ }
32
+ }
33
+ end
34
+ _format(result, sep, options)
35
+ end
36
+
37
+ def _format(result, sep, options={})
38
+ result.map { |row|
39
+ row.join(" ")
40
+ }.join(sep)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ # vim:set ft=ruby :
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "yaml"
4
+
5
+ module Hotdog
6
+ module Formatters
7
+ class Yaml < BaseFormatter
8
+ def format(result, options={})
9
+ result.to_yaml
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ # vim:set ft=ruby :
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Hotdog
4
+ module Formatters
5
+ class BaseFormatter
6
+ def format(result, options={})
7
+ raise(NotImplementedError)
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ # vim:set ft=ruby :
@@ -0,0 +1,3 @@
1
+ module Hotdog
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hotdog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Yamashita Yuu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dogapi
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.13.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.13.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: parslet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.6.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.6.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.10
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.10
83
+ description: Yet another command-line tool for Datadog
84
+ email:
85
+ - peek824545201@gmail.com
86
+ executables:
87
+ - hotdog
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/hotdog
97
+ - hotdog.gemspec
98
+ - lib/hotdog/application.rb
99
+ - lib/hotdog/commands.rb
100
+ - lib/hotdog/commands/destroy.rb
101
+ - lib/hotdog/commands/gc.rb
102
+ - lib/hotdog/commands/help.rb
103
+ - lib/hotdog/commands/hosts.rb
104
+ - lib/hotdog/commands/init.rb
105
+ - lib/hotdog/commands/ls.rb
106
+ - lib/hotdog/commands/rm.rb
107
+ - lib/hotdog/commands/search.rb
108
+ - lib/hotdog/commands/tags.rb
109
+ - lib/hotdog/commands/update.rb
110
+ - lib/hotdog/formatters.rb
111
+ - lib/hotdog/formatters/json.rb
112
+ - lib/hotdog/formatters/plain.rb
113
+ - lib/hotdog/formatters/yaml.rb
114
+ - lib/hotdog/version.rb
115
+ homepage: https://github.com/yyuu/hotdog
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.4.4
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Yet another command-line tool for Datadog
139
+ test_files: []