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.
@@ -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