nexus_client 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +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
|
+
[](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
|
+
|