chef_stash 0.1.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 +7 -0
- data/.gitattributes +17 -0
- data/.gitignore +193 -0
- data/Gemfile +3 -0
- data/LICENSE +201 -0
- data/README.md +85 -0
- data/Rakefile +46 -0
- data/chef_stash.gemspec +52 -0
- data/lib/chef_stash/core_ext/hash.rb +33 -0
- data/lib/chef_stash/core_ext/numeric.rb +50 -0
- data/lib/chef_stash/disk_store.rb +169 -0
- data/lib/chef_stash/memoizable.rb +44 -0
- data/lib/chef_stash/os.rb +45 -0
- data/lib/chef_stash/rash.rb +195 -0
- data/lib/chef_stash/time_cache.rb +355 -0
- data/lib/chef_stash/utils.rb +97 -0
- data/lib/chef_stash/version.rb +42 -0
- data/lib/chef_stash.rb +125 -0
- data/rash.rb +59 -0
- data/repl.rb +147 -0
- metadata +220 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'tmpdir'
|
21
|
+
|
22
|
+
module ChefStash
|
23
|
+
# Disk stashing method variable caching hash, string, array store.
|
24
|
+
#
|
25
|
+
class DiskStore
|
26
|
+
|
27
|
+
# @return [String] location of DiskStash::Cache.store
|
28
|
+
#
|
29
|
+
attr_reader :store
|
30
|
+
|
31
|
+
# Initializes a new disked backed stash hash cache store.
|
32
|
+
#
|
33
|
+
# @param path [String] location for stash store cache.
|
34
|
+
#
|
35
|
+
# @return nothing.
|
36
|
+
#
|
37
|
+
def initialize(store = file_store)
|
38
|
+
@store = store
|
39
|
+
ensure_store_directory
|
40
|
+
end
|
41
|
+
|
42
|
+
# Retrieves a value from the cache, if available and not expired, or yields
|
43
|
+
# to a block that calculates the value to be stored in the cache.
|
44
|
+
#
|
45
|
+
# @param [Object] key
|
46
|
+
# The key to look up or store at.
|
47
|
+
#
|
48
|
+
# @yield yields when the value is not present.
|
49
|
+
#
|
50
|
+
# @yieldreturn [Object]
|
51
|
+
# The value to store in the cache.
|
52
|
+
#
|
53
|
+
# @return [Object]
|
54
|
+
# The value at the key.
|
55
|
+
#
|
56
|
+
def cache(key = nil, &code)
|
57
|
+
key ||= Stash.caller_name
|
58
|
+
@store[key.to_sym] ||= code.call
|
59
|
+
end
|
60
|
+
|
61
|
+
# Clear the whole stash or the value of a key
|
62
|
+
#
|
63
|
+
# @param key [Symbol, String] (optional) string or symbol
|
64
|
+
# representing the key to clear
|
65
|
+
#
|
66
|
+
# @return [Hash] with a key, return the value it had, without
|
67
|
+
# returns {}
|
68
|
+
#
|
69
|
+
def clear!(key = nil)
|
70
|
+
if key.nil?
|
71
|
+
Dir[File.join(store, '*.cache')].each do |file|
|
72
|
+
File.delete(file)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
File.delete(cache_file(key)) if File.exist?(cache_file(key))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Retrieves the value for a given key, if nothing is set,
|
80
|
+
# returns nil
|
81
|
+
#
|
82
|
+
# @param key [Symbol, String] representing the key
|
83
|
+
#
|
84
|
+
# @return [Hash, Array, String] value for key
|
85
|
+
#
|
86
|
+
def [](key)
|
87
|
+
if key.is_a? Array
|
88
|
+
hash = {}
|
89
|
+
key.each do |k|
|
90
|
+
hash[k] = Marshal.load(read_cache_file(k))
|
91
|
+
end
|
92
|
+
hash unless hash.empty?
|
93
|
+
else
|
94
|
+
Marshal.load(read_cache_file(key))
|
95
|
+
end
|
96
|
+
rescue Errno::ENOENT
|
97
|
+
nil # key hasn't been created
|
98
|
+
end
|
99
|
+
|
100
|
+
# Store the given value with the given key, either an an argument
|
101
|
+
# or block. If a previous value was set it will be overwritten
|
102
|
+
# with the new value.
|
103
|
+
#
|
104
|
+
# @param key [Symbol, String] representing the key
|
105
|
+
# @param value [Object] that represents the value (optional)
|
106
|
+
# @param block [&block] that returns the value to set (optional)
|
107
|
+
#
|
108
|
+
# @return nothing.
|
109
|
+
#
|
110
|
+
def []=(key, value)
|
111
|
+
write_cache_file(key, Marshal.dump(value))
|
112
|
+
end
|
113
|
+
|
114
|
+
# returns path to cache file with 'key'
|
115
|
+
def cache_file(key)
|
116
|
+
File.join(store, key.to_s + '.cache')
|
117
|
+
end
|
118
|
+
|
119
|
+
private # P R O P R I E T À P R I V A T A divieto di accesso
|
120
|
+
|
121
|
+
# Yada...yada...
|
122
|
+
#
|
123
|
+
def file_store
|
124
|
+
if ChefStash::OS.windows?
|
125
|
+
win_friendly_path('/chef/._stash_')
|
126
|
+
elsif ChefStash::OS.mac?
|
127
|
+
File.join(File::SEPARATOR, 'var', 'tmp', '._stash')
|
128
|
+
else
|
129
|
+
File.join(File::SEPARATOR, 'var', 'lib', '._stash')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# returns windows friendly version of the provided path, ensures
|
134
|
+
# backslashes are used everywhere
|
135
|
+
#
|
136
|
+
def win_friendly_path(path)
|
137
|
+
system_drive = ENV['SYSTEMDRIVE'] ? ENV['SYSTEMDRIVE'] : ''
|
138
|
+
path = File.join(system_drive, path)
|
139
|
+
path.gsub!(File::SEPARATOR, (File::ALT_SEPARATOR || '\\'))
|
140
|
+
end
|
141
|
+
|
142
|
+
def write_cache_file(key, content)
|
143
|
+
mode = OS.windows? ? 'wb' : 'w+'
|
144
|
+
f = File.open(cache_file(key), mode)
|
145
|
+
f.flock(File::LOCK_EX)
|
146
|
+
f.write(content)
|
147
|
+
f.close
|
148
|
+
content
|
149
|
+
end
|
150
|
+
|
151
|
+
def read_cache_file(key)
|
152
|
+
mode = OS.windows? ? 'rb' : 'r'
|
153
|
+
f = File.open(cache_file(key), mode)
|
154
|
+
f.flock(File::LOCK_SH)
|
155
|
+
out = f.read
|
156
|
+
f.close
|
157
|
+
out
|
158
|
+
end
|
159
|
+
|
160
|
+
def read_cache_mtime(key)
|
161
|
+
nil unless File.exist?(cache_file(key))
|
162
|
+
File.mtime(cache_file(key))
|
163
|
+
end
|
164
|
+
|
165
|
+
def ensure_store_directory
|
166
|
+
Dir.mkdir(store) unless File.directory?(store)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module ChefStash
|
21
|
+
# Memoization is an optimization that saves the return value of a
|
22
|
+
# method so it doesn't need to be re-computed every time that method
|
23
|
+
# is called.
|
24
|
+
#
|
25
|
+
module Memoizable
|
26
|
+
# Create a new memoized method. To use, extend class with Memoizable,
|
27
|
+
# then, in initialize, call memoize
|
28
|
+
#
|
29
|
+
# @return [undefined]
|
30
|
+
#
|
31
|
+
def memoize(methods, cache = nil)
|
32
|
+
cache ||= ChefStash.new(store: ChefStash::DiskStore.new)
|
33
|
+
methods.each do |name|
|
34
|
+
uncached_name = "#{name}_uncached".to_sym
|
35
|
+
singleton_class.class_eval do
|
36
|
+
alias_method uncached_name, name
|
37
|
+
define_method(name) do |*a, &b|
|
38
|
+
cache.cache(name) { send uncached_name, *a, &b }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'rbconfig'
|
21
|
+
|
22
|
+
module ChefStash
|
23
|
+
# Finds out the current Operating System.
|
24
|
+
module OS
|
25
|
+
def self.windows?
|
26
|
+
windows = /cygwin|mswin|mingw|bccwin|wince|emx/i
|
27
|
+
(RbConfig::CONFIG['host_os'] =~ windows) != nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.mac?
|
31
|
+
mac = /darwin|mac os/i
|
32
|
+
(RbConfig::CONFIG['host_os'] =~ mac) != nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.unix?
|
36
|
+
unix = /solaris|bsd/i
|
37
|
+
(RbConfig::CONFIG['host_os'] =~ unix) != nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.linux?
|
41
|
+
linux = /linux/i
|
42
|
+
(RbConfig::CONFIG['host_os'] =~ linux) != nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'anemone'
|
21
|
+
|
22
|
+
module ChefStash
|
23
|
+
class Rash
|
24
|
+
include Memoizable
|
25
|
+
|
26
|
+
# Initializes a new store object.
|
27
|
+
#
|
28
|
+
# @param [String, URI::HTTP] url
|
29
|
+
# The URL to the repository to scan.
|
30
|
+
#
|
31
|
+
# @param [String] path
|
32
|
+
# The path to append to the URL.
|
33
|
+
#
|
34
|
+
# @return [ChefStash]
|
35
|
+
#
|
36
|
+
def initialize(url, path)
|
37
|
+
memoize [:fetch]
|
38
|
+
@store ||= fetch(url, path)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Retrieves a value from the cache.
|
42
|
+
#
|
43
|
+
# @param [Object] key
|
44
|
+
# The key to look up.
|
45
|
+
#
|
46
|
+
# @return [Object, nil]
|
47
|
+
# The value at the key, when present, or `nil`.
|
48
|
+
#
|
49
|
+
def [](key)
|
50
|
+
@store[key]
|
51
|
+
end
|
52
|
+
alias_method :get, :[]
|
53
|
+
|
54
|
+
# Stores a value in the cache, either an an argument or block. If a
|
55
|
+
# previous value was set it will be overwritten with the new value.
|
56
|
+
#
|
57
|
+
# @param [Object] key
|
58
|
+
# The key to store.
|
59
|
+
#
|
60
|
+
# @param val [Object]
|
61
|
+
# The value to store.
|
62
|
+
#
|
63
|
+
# @return [Object, nil]
|
64
|
+
# The value at the key.
|
65
|
+
#
|
66
|
+
def []=(key, value)
|
67
|
+
@store[key] = value
|
68
|
+
end
|
69
|
+
alias_method :set, :[]=
|
70
|
+
|
71
|
+
# Removes a value from the cache.
|
72
|
+
#
|
73
|
+
# @param [Object] key
|
74
|
+
# The key to remove.
|
75
|
+
#
|
76
|
+
# @return [Object, nil]
|
77
|
+
# The value at the key, when present, or `nil`.
|
78
|
+
#
|
79
|
+
def delete(key)
|
80
|
+
@store.delete(key)
|
81
|
+
end
|
82
|
+
|
83
|
+
# return the size of the store as an integer
|
84
|
+
#
|
85
|
+
# @return [Fixnum]
|
86
|
+
#
|
87
|
+
def size
|
88
|
+
@store.size
|
89
|
+
end
|
90
|
+
|
91
|
+
# return all keys in the store as an array
|
92
|
+
#
|
93
|
+
# @return [Array<String, Symbol>] all the keys in store
|
94
|
+
#
|
95
|
+
def keys
|
96
|
+
@store.keys
|
97
|
+
end
|
98
|
+
|
99
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
100
|
+
|
101
|
+
# Loads a Chef stash hash of cache stash of hash data into the hash stash
|
102
|
+
# key/value stach hash cache object Chef store, or create a new one.
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
# rash[:av]
|
106
|
+
# => {
|
107
|
+
# :ini => {
|
108
|
+
# :code => "200 OK",
|
109
|
+
# :depth => 3,
|
110
|
+
# :size => "0.0 MB",
|
111
|
+
# :key => :av,
|
112
|
+
# :md5 => "336d9da322febc949eb22ae3f47d293b",
|
113
|
+
# :modified => "Mon, 16 Feb 2015 07:13:23 GMT",
|
114
|
+
# :name => "AV.ini",
|
115
|
+
# :referer => "http://winini.mudbox.dev/packages_3.0/AV/",
|
116
|
+
# :response_time => "1 seconds 0 milliseconds",
|
117
|
+
# :sha256 => "905425e1a33b0662297181c3031066d630f8241880c7001",
|
118
|
+
# :content_type => "text/inifile",
|
119
|
+
# :url => "http://winini.mudbox.dev/packages_3.0/AV/AV.ini",
|
120
|
+
# :utime => "Mon, 20 Apr 2015 06:33:07 GMT",
|
121
|
+
# :visited => nil
|
122
|
+
# },
|
123
|
+
# :zip => {
|
124
|
+
# :code => "200 OK",
|
125
|
+
# :depth => 3,
|
126
|
+
# :size => "32.6 MB",
|
127
|
+
# :key => :av,
|
128
|
+
# :md5 => "2488ceb74eb6cb5fae463c88b806ebff",
|
129
|
+
# :modified => "Mon, 16 Feb 2015 07:13:29 GMT",
|
130
|
+
# :name => "AV.zip",
|
131
|
+
# :referer => "http://winini.mudbox.dev/packages_3.0/AV/",
|
132
|
+
# :response_time => "1 seconds 0 milliseconds",
|
133
|
+
# :sha256 => "f3f14ac64263fc7b091d150a1bc0867d38af57466574587",
|
134
|
+
# :content_type => "application/zip",
|
135
|
+
# :url => "http://winini.mudbox.dev/packages_3.0/AV/AV.zip",
|
136
|
+
# :utime => "Mon, 20 Apr 2015 06:33:08 GMT",
|
137
|
+
# :visited => nil
|
138
|
+
# }
|
139
|
+
# }
|
140
|
+
#
|
141
|
+
# @param [URI::HTTP] url
|
142
|
+
# The URL to crawl to build a Repository Hash (RASH) cache hash store.
|
143
|
+
#
|
144
|
+
# @param [String] path
|
145
|
+
# A path to filter on.
|
146
|
+
#
|
147
|
+
# @return [RASH]
|
148
|
+
#
|
149
|
+
def fetch(url, path)
|
150
|
+
results = []
|
151
|
+
regex = /#{path}\/\w+.(\w+.(ini|zip)|sha256.txt)$/i
|
152
|
+
options = { threads: 20, depth_limit: 3, discard_page_bodies: true }
|
153
|
+
|
154
|
+
Anemone.crawl(url, options) do |anemone|
|
155
|
+
anemone.on_pages_like(regex) do |page|
|
156
|
+
url = page.url.to_s
|
157
|
+
name = File.basename(url)
|
158
|
+
key = File.basename(name, '.*').downcase.to_sym
|
159
|
+
type = File.extname(name)[1..-1].downcase.to_sym
|
160
|
+
|
161
|
+
header = page.headers
|
162
|
+
bytes = header['content-length'].first
|
163
|
+
modified = header['last-modified'].first
|
164
|
+
created = Time.now.utc.httpdate
|
165
|
+
content = type == :ini ? 'text/inifile' : header['content-type'].first
|
166
|
+
size = ChefStash::FileSize.new(bytes).to_size(:mb).to_s
|
167
|
+
|
168
|
+
results << { key => { type => {
|
169
|
+
code: ChefStash::Response.code(page.code),
|
170
|
+
depth: page.depth,
|
171
|
+
size: size,
|
172
|
+
key: key,
|
173
|
+
md5: Digest::MD5.hexdigest(page.body.to_s),
|
174
|
+
modified: modified,
|
175
|
+
name: name,
|
176
|
+
referer: page.referer.to_s,
|
177
|
+
response_time: page.response_time.time_humanize,
|
178
|
+
sha256: OpenSSL::Digest::SHA256.new(page.body).to_s,
|
179
|
+
content_type: content,
|
180
|
+
url: url,
|
181
|
+
created: created,
|
182
|
+
visited: page.visited
|
183
|
+
} } }
|
184
|
+
|
185
|
+
seen_urls << { url => { modified: modified, created: created }}
|
186
|
+
seen_urls << { key => { type => {
|
187
|
+
modified: modified, created: created
|
188
|
+
} } }
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
results.reduce({}, :recursive_merge)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|