nexus_client 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +12 -0
- data/Gemfile +7 -0
- data/LICENSE +505 -0
- data/README.md +129 -0
- data/Rakefile +12 -0
- data/bin/nexus-client +38 -0
- data/lib/nexus_client.rb +194 -0
- data/lib/nexus_client/analytics.rb +187 -0
- data/lib/nexus_client/cache.rb +105 -0
- data/lib/nexus_client/gav.rb +44 -0
- data/lib/nexus_client/version.rb +9 -0
- data/nexus_client.gemspec +30 -0
- data/spec/integration/analytics_spec.rb +137 -0
- data/spec/integration/cache_spec.rb +37 -0
- data/spec/integration/nexus_client_bin_spec.rb +0 -0
- data/spec/integration/nexus_client_spec.rb +76 -0
- data/spec/integration/real_analytics_spec.rb +45 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/analytics_spec.rb +51 -0
- data/spec/unit/cache_spec.rb +110 -0
- data/spec/unit/gav_spec.rb +55 -0
- data/spec/unit/nexus_client_spec.rb +54 -0
- metadata +192 -0
data/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/logicminds/nexus-client.svg)](https://travis-ci.org/logicminds/nexus-client)
|
2
|
+
|
3
|
+
|
4
|
+
# NexusClient
|
5
|
+
The nexus client is a ruby wrapper around the nexus REST API for downloading maven artifacts.
|
6
|
+
It features the ability to cache artifacts and also performs artifact checksums to ensure you only
|
7
|
+
download the artifact once. This gem was originally designed for use with configuration management software like puppet.
|
8
|
+
|
9
|
+
This gem does not require maven or any of the maven settings files. It was originally created to use with
|
10
|
+
configuration management software to download artifacts to hundreds of servers. A CLI tool was created for this purpose
|
11
|
+
to ease downloading of artifacts on any system.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'nexus_client'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install nexus_client
|
26
|
+
|
27
|
+
## Features
|
28
|
+
### Cache repository
|
29
|
+
The cache repository feature if enabled can be used to cache artifacts much like maven. However since this
|
30
|
+
gem does not use maven you are free to store the artifacts where ever you want. This works great for downloading the same
|
31
|
+
artifact on the same system. However, the cache feature was built to be used across a shared file system like NFS
|
32
|
+
so if multiple systems are downloading the same artifact you can reduce the time and bandwidth needed to download large
|
33
|
+
artifacts. This feature alone is like having a mini nexus proxy on your network!
|
34
|
+
|
35
|
+
### Automatic artifact checksums
|
36
|
+
This gem will grab the sha1 checksum from the nexus server and compare the checksum
|
37
|
+
with the downloaded artifact. If the checksums are different and error will be raised. If the checksums match, a file
|
38
|
+
will be created with a extension of sha1 after the artifact is downloaded. This sha1 file additionally contains the sha1 checksum of the file.
|
39
|
+
This sha1 file is created as a trigger mechanism for configuration management software and also to speed up sha1 computation time of the artifact.
|
40
|
+
```shell
|
41
|
+
Coreys-MacBook-Pro-2:tmp$ nexus-client --nexus-host https://repository.jboss.org/nexus -e -c /tmp/cache -g 'org.glassfish.main.external:ant:4.0:central::pom' -d /tmp
|
42
|
+
Coreys-MacBook-Pro-2:tmp$ ls -l
|
43
|
+
-rw-r--r-- 1 user1 wheel 8853 Oct 22 14:26 ant-4.0.pom
|
44
|
+
-rw-r--r-- 1 user1 wheel 40 Oct 22 14:26 ant-4.0.pom.sha1
|
45
|
+
|
46
|
+
Coreys-MacBook-Pro-2:tmp$ more ant-4.0.pom.sha1
|
47
|
+
387951c0aa333024b25085d76a8ad77441b9e55f
|
48
|
+
```
|
49
|
+
|
50
|
+
### Smart Artifact Retrieval
|
51
|
+
This gem will use artifact checksums to ensure the artifact is only downloaded once. This
|
52
|
+
is really important during configuration management runtime when the artifact downloading process is expected run multiple times.
|
53
|
+
This is even more important when you use artifact snapshots. Artifacts that are snapshots can contain different checksums
|
54
|
+
at any time so its important that we download only when a new snapshot is detected by comparing the checksums.
|
55
|
+
|
56
|
+
### Cache Analytics(Experimental)
|
57
|
+
This feature records the cache usage and shows just how much bandwidth has been saved and what
|
58
|
+
artifacts are currently cached. This feature is experimental and is not feature complete. It was originally designed
|
59
|
+
to show historically analysis of artifacts and make this information available to shell scripts, graphite and other reporting mechanisms.
|
60
|
+
This could be used later to send alerts when artifact sizes between versions/snapshots are significantly different from each other.
|
61
|
+
|
62
|
+
|
63
|
+
## Ruby Usage
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
client = Nexus::Client.new
|
67
|
+
client.download_gav('/tmp/ant-4.0.pom', 'org.glassfish.main.external:ant:4.0:central::pom')
|
68
|
+
```
|
69
|
+
or
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
Nexus::Client.download_gav('/tmp/ant-4.0.pom', 'org.glassfish.main.external:ant:4.0:central::pom')
|
73
|
+
```
|
74
|
+
|
75
|
+
## CLI Usage
|
76
|
+
We have also created a simple CLI tool that makes it easy to download artifacts from any nexus server.
|
77
|
+
|
78
|
+
```shell
|
79
|
+
nexus-client --help
|
80
|
+
Options:
|
81
|
+
--destination, -d <s>: destination directory to download file to
|
82
|
+
--gav-string, -g <s>: The nexus GAV value: group:artifact:version:repository:classifier:extension
|
83
|
+
--cache-dir, -c <s>: The directory to cache files to
|
84
|
+
--enable-cache, -e: Enable cache
|
85
|
+
--nexus-host, -n <s>: Nexus host url, if left blank reads from ~/.nexus_host
|
86
|
+
--enable-analytics, -a: Enable cache analytics, requires sqlite3 gem (experimental!)
|
87
|
+
--help, -h: Show this message
|
88
|
+
|
89
|
+
nexus-client --nexus-host https://repository.jboss.org/nexus -e -c /tmp/cache -g 'org.glassfish.main.external:ant:4.0:central::pom' -d /tmp
|
90
|
+
```
|
91
|
+
|
92
|
+
## Tips
|
93
|
+
|
94
|
+
Create a nexus host file to store the nexus host url. This can same time if your nexus host url is the same. By default
|
95
|
+
the nexus-client CLI will override the stored url in your nexus_host file when passing in the --nexus-host argument.
|
96
|
+
|
97
|
+
```shell
|
98
|
+
Coreys-MacBook-Pro-2:~$ pwd
|
99
|
+
/Users/user1
|
100
|
+
Coreys-MacBook-Pro-2: echo 'https://repository.jboss.org/nexus' > ~/.nexus_host
|
101
|
+
Coreys-MacBook-Pro-2:~$ more .nexus_host
|
102
|
+
https://repository.jboss.org/nexus
|
103
|
+
```
|
104
|
+
## TODO
|
105
|
+
* Extend usage to other supported maven server solutions (artifactory, archiva)
|
106
|
+
* Implement a feature toggle to enable parallel downloads.
|
107
|
+
* Finish analytics feature
|
108
|
+
* Remove usage of typheous and other C compiled dependent gems (conflicts with parallel feature)
|
109
|
+
* Add feature to upload artifacts to nexus server
|
110
|
+
* Add basic user authentication
|
111
|
+
|
112
|
+
|
113
|
+
## OS Support
|
114
|
+
Should work on all *nix platforms. Windows may also be supported but it has never been tested.
|
115
|
+
If you use this gem on windows please let me know so I can update this doc.
|
116
|
+
|
117
|
+
## Contributing
|
118
|
+
|
119
|
+
1. Fork it
|
120
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
121
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
122
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
123
|
+
5. Create new Pull Request
|
124
|
+
|
125
|
+
|
126
|
+
## Examples
|
127
|
+
|
128
|
+
|
129
|
+
|
data/Rakefile
ADDED
data/bin/nexus-client
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'nexus_client'
|
4
|
+
require 'trollop'
|
5
|
+
require 'etc'
|
6
|
+
|
7
|
+
# read host will read ~/.nexus_host file and
|
8
|
+
def read_host(filename="#{Etc.getpwuid.dir}/.nexus_host")
|
9
|
+
fn = File.expand_path(filename)
|
10
|
+
abort("Please create the file #{filename} and add your nexus host") if not File.exists?(filename)
|
11
|
+
begin
|
12
|
+
File.open(fn, 'r') { |f| f.read }.strip
|
13
|
+
rescue Exception => e
|
14
|
+
raise(e)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
opts = Trollop::options do
|
19
|
+
opt :destination, "destination directory to download file to " , :type => :string, :required => true
|
20
|
+
opt :gav_string, "The nexus GAV value: group:artifact:version:repository:classifier:extension" , :type => :string, :required => true
|
21
|
+
opt :cache_dir, "The directory to cache files to" , :type => :string
|
22
|
+
opt :enable_cache, "Enable cache", :default => false, :type => :boolean
|
23
|
+
opt :nexus_host, "Nexus host url, if left blank reads from ~/.nexus_host", :type => :string
|
24
|
+
opt :enable_analytics, "Enable cache analytics, requires sqlite3 gem (experimental!)", :type => :boolean, :default => false
|
25
|
+
end
|
26
|
+
|
27
|
+
if opts[:nexus_host].nil? or opts[:nexus_host].empty?
|
28
|
+
opts[:nexus_host] = read_host
|
29
|
+
end
|
30
|
+
|
31
|
+
value = Nexus::Client.download(opts[:destination], opts[:gav_string],
|
32
|
+
opts[:cache_dir], opts[:enable_cache],
|
33
|
+
opts[:enable_analytics], opts[:nexus_host])
|
34
|
+
|
35
|
+
if value
|
36
|
+
exit(0)
|
37
|
+
end
|
38
|
+
exit(1)
|
data/lib/nexus_client.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
require "nexus_client/version"
|
2
|
+
require "nexus_client/gav"
|
3
|
+
require 'nexus_client/cache'
|
4
|
+
require 'nexus_client/analytics'
|
5
|
+
require "tmpdir"
|
6
|
+
require 'typhoeus'
|
7
|
+
require 'json'
|
8
|
+
require 'etc'
|
9
|
+
require 'fileutils'
|
10
|
+
|
11
|
+
module Nexus
|
12
|
+
class Client
|
13
|
+
attr_reader :host, :cache
|
14
|
+
attr_accessor :use_cache, :log
|
15
|
+
|
16
|
+
def initialize(nexus_host=nil, cache_dir='/tmp/cache', enable_cache=true, enable_analytics=false,logger=nil)
|
17
|
+
@log = logger
|
18
|
+
@host = nexus_host || default_host
|
19
|
+
@host = @host.gsub(/\/nexus$/, '') # just in case user enters /nexus
|
20
|
+
@use_cache = enable_cache
|
21
|
+
if @use_cache
|
22
|
+
@cache_base = cache_dir
|
23
|
+
@cache = Nexus::Cache.new(@cache_base, enable_analytics, log)
|
24
|
+
end
|
25
|
+
#Typhoeus::Config.verbose = true
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
# read host will read ~/.nexus_host file and
|
30
|
+
def read_host(filename="#{Etc.getpwuid.dir}/.nexus_host")
|
31
|
+
fn = File.expand_path(filename)
|
32
|
+
abort("Please create the file #{filename} and add your nexus host") if not File.exists?(filename)
|
33
|
+
begin
|
34
|
+
File.open(fn, 'r') { |f| f.read }.strip
|
35
|
+
rescue Exception => e
|
36
|
+
raise(e)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_host
|
41
|
+
read_host
|
42
|
+
end
|
43
|
+
|
44
|
+
def log
|
45
|
+
if @log.nil?
|
46
|
+
@log = Logger.new(STDOUT)
|
47
|
+
end
|
48
|
+
@log
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.download(destination, gav_str, cache_dir='/tmp/cache', enable_cache=false,enable_analytics=false,host=nil)
|
52
|
+
client = Nexus::Client.new(host, cache_dir, enable_cache,enable_analytics)
|
53
|
+
client.download_gav(destination, gav_str)
|
54
|
+
end
|
55
|
+
|
56
|
+
def download_gav(destination, gav_str)
|
57
|
+
gav = Nexus::Gav.new(gav_str)
|
58
|
+
download(destination, gav)
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_target(destination)
|
62
|
+
destination = File.expand_path(destination)
|
63
|
+
if ! File.directory?(destination)
|
64
|
+
begin
|
65
|
+
FileUtils.mkdir_p(destination) if not File.exists?(destination)
|
66
|
+
rescue SystemCallError => e
|
67
|
+
raise e, 'Cannot create directory'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# retrieves the attributes of the gav
|
74
|
+
def gav_data(gav)
|
75
|
+
res = {}
|
76
|
+
request = Typhoeus::Request.new(
|
77
|
+
"#{host}/nexus/service/local/artifact/maven/resolve",
|
78
|
+
:params => gav.to_hash,:connecttimeout => 5,
|
79
|
+
:headers => { 'Accept' => 'application/json' }
|
80
|
+
)
|
81
|
+
request.on_failure do |response|
|
82
|
+
raise("Failed to get gav data for #{gav.to_s}")
|
83
|
+
end
|
84
|
+
request.on_complete do |response|
|
85
|
+
res = JSON.parse(response.response_body)
|
86
|
+
end
|
87
|
+
request.run
|
88
|
+
|
89
|
+
res['data']
|
90
|
+
end
|
91
|
+
|
92
|
+
# returns the sha1 of the file
|
93
|
+
def sha(file, use_sha_file=false)
|
94
|
+
if use_sha_file and File.exists?("#{file}.sha1")
|
95
|
+
# reading the file is faster than doing a hash, so we keep the hash in the file
|
96
|
+
# then we read back and compare. There is no reason to perform sha1 everytime
|
97
|
+
begin
|
98
|
+
File.open("#{file}.sha1", 'r') { |f| f.read().strip}
|
99
|
+
rescue
|
100
|
+
Digest::SHA1.file(File.expand_path(file)).hexdigest
|
101
|
+
end
|
102
|
+
else
|
103
|
+
Digest::SHA1.file(File.expand_path(file)).hexdigest
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# sha_match? returns bool by comparing the sha1 of the nexus gav artifact and the local file
|
108
|
+
def sha_match?(file, gav, use_sha_file=false)
|
109
|
+
if File.exists?(file)
|
110
|
+
if gav.sha1.nil?
|
111
|
+
gav.sha1 = gav_data(gav)['sha1']
|
112
|
+
end
|
113
|
+
sha(file,use_sha_file) == gav.sha1
|
114
|
+
else
|
115
|
+
false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# writes the sha1 a file if and only if the contents of the file do not match
|
122
|
+
def write_sha1(file,sha1)
|
123
|
+
shafile = "#{file}.sha1"
|
124
|
+
File.open(shafile, 'w') { |f| f.write(sha1) }
|
125
|
+
end
|
126
|
+
|
127
|
+
# downloads the gav to the destination, returns the file if download was successful
|
128
|
+
# if cache is on then it will use the cache and if file is new will also cache the new file
|
129
|
+
# TODO need a timeout when host is unreachable
|
130
|
+
def download(destination, gav)
|
131
|
+
raise 'Download destination must not be empty' if destination.empty?
|
132
|
+
create_target(destination) # ensure directory path is created
|
133
|
+
destination = File.expand_path(destination)
|
134
|
+
if File.directory?(destination)
|
135
|
+
dstfile = File.expand_path("#{destination}/#{gav.filename}")
|
136
|
+
else
|
137
|
+
dstfile = File.expand_path(destination)
|
138
|
+
end
|
139
|
+
# if the file already exists at the destination path than we don't need to download it again
|
140
|
+
if sha_match?(dstfile, gav)
|
141
|
+
# create a file that stores the sha1 for faster file comparisions later
|
142
|
+
# This will only get created when the sha1 matches
|
143
|
+
write_sha1("#{dstfile}", gav.sha1)
|
144
|
+
if use_cache and not cache.exists?(gav)
|
145
|
+
cache.add_file(gav, dstfile)
|
146
|
+
end
|
147
|
+
return true
|
148
|
+
end
|
149
|
+
# remove the previous sha1 file if it already exists
|
150
|
+
FileUtils.rm("#{dstfile}.sha1") if File.exists?("#{dstfile}.sha1")
|
151
|
+
|
152
|
+
if gav.sha1.nil?
|
153
|
+
gav.sha1 = gav_data(gav)['sha1']
|
154
|
+
end
|
155
|
+
# use the cache if the file is in the cache
|
156
|
+
if use_cache and cache.exists?(gav)
|
157
|
+
cache_file_path = cache.file_path(gav)
|
158
|
+
FileUtils.copy(cache_file_path, dstfile)
|
159
|
+
cache.record_hit(gav)
|
160
|
+
else
|
161
|
+
request = Typhoeus::Request.new(
|
162
|
+
"#{host}/nexus/service/local/artifact/maven/redirect",
|
163
|
+
:params => gav.to_hash,
|
164
|
+
:connecttimeout => 5,
|
165
|
+
:followlocation => true
|
166
|
+
)
|
167
|
+
request.on_failure do |response|
|
168
|
+
raise("Failed to download #{gav.to_s}")
|
169
|
+
end
|
170
|
+
|
171
|
+
# when complete, lets write the data to the file
|
172
|
+
# first lets compare the sha matches
|
173
|
+
# if the gav we thought we downloaded has the same checksum, were are good
|
174
|
+
request.on_complete do |response|
|
175
|
+
File.open(dstfile, 'wb') { |f| f.write(response.body) } unless ! response.success?
|
176
|
+
if not sha_match?(dstfile, gav, false)
|
177
|
+
raise("Error sha1 mismatch gav #{gav.sha1} != #{sha(dstfile)}")
|
178
|
+
end
|
179
|
+
gav.attributes[:size] = File.size(dstfile)
|
180
|
+
gav.attributes[:total_time] = response.options[:total_time]
|
181
|
+
|
182
|
+
# lets cache the file if cache is on
|
183
|
+
if use_cache
|
184
|
+
cache.add_file(gav, dstfile)
|
185
|
+
end
|
186
|
+
dstfile
|
187
|
+
end
|
188
|
+
request.run
|
189
|
+
dstfile
|
190
|
+
end
|
191
|
+
dstfile
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Nexus
|
4
|
+
class Analytics
|
5
|
+
attr_reader :db, :data, :db_file
|
6
|
+
attr_accessor :a_file, :log
|
7
|
+
|
8
|
+
# new method
|
9
|
+
def initialize(database_dir='./',db_filename='cache-analytics.db', logger=nil)
|
10
|
+
@log = logger
|
11
|
+
|
12
|
+
filename ||= db_filename || 'cache-analytics.db'
|
13
|
+
|
14
|
+
log.warn "Filename is nil" if filename.nil?
|
15
|
+
@db_file = File.join(database_dir,filename)
|
16
|
+
begin
|
17
|
+
require 'sqlite3'
|
18
|
+
@db = SQLite3::Database.new( @db_file)
|
19
|
+
init_tables
|
20
|
+
total_view
|
21
|
+
rescue LoadError => e
|
22
|
+
log.error 'The sqlite3 gem must be installed before using the analytics class'
|
23
|
+
raise(e.message)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def log
|
28
|
+
if @log.nil?
|
29
|
+
@log = Logger.new(STDOUT)
|
30
|
+
end
|
31
|
+
@log
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_item(gav, file_path)
|
35
|
+
begin
|
36
|
+
db.execute("insert into artifacts (sha1,artifact_gav,filesize,request_time, modified, file_location) "+
|
37
|
+
"values ('#{gav.sha1}','#{gav.to_s}', #{gav.attributes[:size]}, #{gav.attributes[:total_time]},"+
|
38
|
+
"'#{Time.now.to_i}', '#{file_path}')")
|
39
|
+
rescue
|
40
|
+
log.warn("Ignoring Duplicate entry #{file_path}")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def gavs
|
46
|
+
db.execute("select artifact_gav from artifacts").flatten
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_item(gav)
|
50
|
+
count = hit_count(gav)
|
51
|
+
db.execute <<SQL
|
52
|
+
UPDATE artifacts SET hit_count=#{count + 1}, modified=#{Time.now.to_i}
|
53
|
+
WHERE sha1='#{gav.sha1}'
|
54
|
+
SQL
|
55
|
+
end
|
56
|
+
|
57
|
+
def totals
|
58
|
+
db.execute("select * from totals")
|
59
|
+
end
|
60
|
+
|
61
|
+
def total(gav)
|
62
|
+
# TODO fix NoMethodError: undefined method `sha1' for #<String:0x7f1a0f387720>
|
63
|
+
# when type is not a gav or sha1 is not available
|
64
|
+
data = db.execute("select * from totals where sha1 = '#{gav.sha1}'")
|
65
|
+
db.results_as_hash = false
|
66
|
+
data
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def hit_count(gav)
|
71
|
+
row = db.execute("select hit_count from totals where sha1 = '#{gav.sha1}'").first
|
72
|
+
if row.nil?
|
73
|
+
0
|
74
|
+
else
|
75
|
+
row.first
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def total_time(gav)
|
80
|
+
row = db.execute("select total_time_saved from totals where sha1 = '#{gav.sha1}'").first
|
81
|
+
if row.nil?
|
82
|
+
0
|
83
|
+
else
|
84
|
+
row.first
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
def total_bytes(gav, pretty=false)
|
90
|
+
row = db.execute("select total_bytes_saved from totals where sha1 = '#{gav.sha1}'").first
|
91
|
+
if row.nil?
|
92
|
+
0
|
93
|
+
else
|
94
|
+
if pretty
|
95
|
+
Filesize.from("#{row.first} B").pretty
|
96
|
+
else
|
97
|
+
row.first
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
# returns the totals view as json
|
104
|
+
# the results as hash returns extra key/values we don't want so
|
105
|
+
# we had to create our own hash
|
106
|
+
# there are better ways of doing this but this was simple to create
|
107
|
+
def to_json(pretty=true)
|
108
|
+
db.results_as_hash = false
|
109
|
+
totals = db.execute("select * from totals")
|
110
|
+
hash_total = []
|
111
|
+
totals.each do |row|
|
112
|
+
h = {}
|
113
|
+
(0...row.length).each do |col|
|
114
|
+
h[total_columns[col]] = row[col]
|
115
|
+
end
|
116
|
+
hash_total << h
|
117
|
+
end
|
118
|
+
if pretty
|
119
|
+
JSON.pretty_generate(hash_total)
|
120
|
+
else
|
121
|
+
hash_total.to_json
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# removes old items from the database that are older than mtime
|
126
|
+
def remove_old_items(mtime)
|
127
|
+
db.execute <<SQL
|
128
|
+
DELETE from artifacts where modified < #{mtime}
|
129
|
+
SQL
|
130
|
+
end
|
131
|
+
|
132
|
+
# get items older than mtime, defaults to 2 days ago
|
133
|
+
def old_items(mtime=(Time.now.to_i)-(172800))
|
134
|
+
data = db.execute <<SQL
|
135
|
+
SELECT * from artifacts where modified < #{mtime}
|
136
|
+
SQL
|
137
|
+
data || []
|
138
|
+
end
|
139
|
+
|
140
|
+
# returns the top X most utilized caches
|
141
|
+
def top_x(amount=10)
|
142
|
+
db.execute <<SQL
|
143
|
+
SELECT * FROM totals
|
144
|
+
ORDER BY hit_count desc
|
145
|
+
LIMIT #{amount}
|
146
|
+
SQL
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def total_columns
|
152
|
+
%w(sha1 artifact_gav filesize request_time modified file_location hit_count total_bytes_saved total_time_saved)
|
153
|
+
end
|
154
|
+
|
155
|
+
def artifact_columns
|
156
|
+
%w(sha1 artifact_gav filesize, request_time hit_count modified file_location )
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
def init_tables
|
161
|
+
# id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
162
|
+
db.execute <<SQL
|
163
|
+
CREATE TABLE IF NOT EXISTS artifacts (
|
164
|
+
sha1 VARCHAR(40) PRIMARY KEY NOT NULL,
|
165
|
+
artifact_gav VARCHAR(255),
|
166
|
+
filesize BIGINT default 0,
|
167
|
+
request_time FLOAT default 0,
|
168
|
+
hit_count INTEGER default 0,
|
169
|
+
modified BIGINT default '#{Time.now.to_i}',
|
170
|
+
file_location VARCHAR(255)
|
171
|
+
);
|
172
|
+
SQL
|
173
|
+
end
|
174
|
+
|
175
|
+
def total_view
|
176
|
+
db.execute <<SQL
|
177
|
+
CREATE VIEW IF NOT EXISTS totals AS
|
178
|
+
SELECT sha1, artifact_gav, filesize, request_time, modified, file_location,
|
179
|
+
hit_count, sum(hit_count * filesize) AS 'total_bytes_saved',
|
180
|
+
sum(hit_count * request_time) as 'total_time_saved'
|
181
|
+
FROM artifacts
|
182
|
+
SQL
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|