chef_stash 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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