cook 2.0.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.
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ # Not required when using Hoe ruby gem
21
+ Manifest.txt
data/History.txt ADDED
@@ -0,0 +1,19 @@
1
+ === 2.0.1 / 2014-01-15 (external GitHub)
2
+
3
+ * 2.0.1 minor testing
4
+
5
+ * first working release (tested on internal cookbooks)
6
+
7
+ === 2.0.0 / 2014-01-15 (external GitHub)
8
+
9
+ * 2 major enhancement
10
+
11
+ * initial preparation for release as a ruby gem
12
+
13
+ === 1.0.0 / 2012-02-01 (internal)
14
+
15
+ * 1 major enhancement
16
+
17
+ * Birthday!
18
+
19
+
data/README.txt ADDED
@@ -0,0 +1,89 @@
1
+ = cook
2
+
3
+ home :: https://github.com/stephengaito/rGems-cook
4
+
5
+ == DESCRIPTION:
6
+
7
+ cook is a rake extension with:
8
+
9
+ 1. configuration file,
10
+ 1. the ability to retrieve passwords from encrypted configuration files,
11
+ 1. the ability to create files using Erubis templates,
12
+ 1. the ablity to interact with both local and remote shells.
13
+
14
+ Its main file is a traditional rake Rakefile, which has recipe
15
+ commands. Each recipe is a collection of rake task files and
16
+ associated YAML configuration files, allowing tasks to make use of
17
+ extensive configuration information. The configuration is built up
18
+ from the various fragments in the conf files assocaited with each set
19
+ of rake tasks.
20
+
21
+ == FEATURES:
22
+
23
+ * Based on standard ruby rake
24
+
25
+ * Adds the ability to build up configuration from a number of separate
26
+ configuration files overlaying the more specific configuration
27
+ parameters over the more general parameters.
28
+
29
+ * Allows collections of related rake tasks and configuration files in
30
+ the form of recipe directories.
31
+
32
+ * Uses Erubis to create files from templates.
33
+
34
+ * Uses Greenletters to enable interaction with both local and remote
35
+ shells.
36
+
37
+ * Uses GPGme to obtain passwords from GPG encrypted configuration
38
+ files.
39
+
40
+ == SYNOPSIS:
41
+
42
+ cook --config dev update
43
+
44
+ or
45
+
46
+ cook -c prod update
47
+
48
+ to cook the update task with the additional dev or prod
49
+ configurations.
50
+
51
+ == REQUIREMENTS:
52
+
53
+ * rake
54
+ * construct
55
+ * erubis
56
+ * greenletters
57
+ * gpgme
58
+ * highline (for requesting GPGme passphrase from command line)
59
+
60
+ == INSTALL:
61
+
62
+ To install the cook ruby gem
63
+
64
+ $ gem install cook
65
+
66
+ == LICENSE:
67
+
68
+ (The MIT License)
69
+
70
+ Copyright (c) 2014 Stephen Gaito
71
+
72
+ Permission is hereby granted, free of charge, to any person obtaining a
73
+ copy of this software and associated documentation files (the
74
+ 'Software'), to deal in the Software without restriction, including
75
+ without limitation the rights to use, copy, modify, merge, publish,
76
+ distribute, sublicense, and/or sell copies of the Software, and to
77
+ permit persons to whom the Software is furnished to do so, subject to
78
+ the following conditions:
79
+
80
+ The above copyright notice and this permission notice shall be included
81
+ in all copies or substantial portions of the Software.
82
+
83
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS
84
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
85
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
86
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
87
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
88
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
89
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ # -*- ruby -*-
2
+
3
+ # To release this ruby gem type:
4
+ # rake release VERSION=x.y.z
5
+ # where x.y.z is the appropriate version number of this gem.
6
+
7
+ require 'rubygems'
8
+ require 'hoe'
9
+
10
+ # Hoe.plugin :compiler
11
+ # Hoe.plugin :gem_prelude_sucks
12
+ # Hoe.plugin :inline
13
+ # Hoe.plugin :minitest
14
+ # Hoe.plugin :racc
15
+ # Hoe.plugin :rubyforge
16
+
17
+ # generate the Manifest.txt file (before we invoke Hoe.spec)
18
+ manifest = FileList[
19
+ '.gitignore',
20
+ 'History.*',
21
+ 'README.*',
22
+ 'Rakefile',
23
+ 'bin/**/*',
24
+ 'lib/**/*.rb',
25
+ 'test/**/*.rb',
26
+ ];
27
+ File.open('Manifest.txt', 'w') do | manifestFile |
28
+ manifestFile.puts("Manifest.txt");
29
+ manifestFile.write(manifest.to_a.join("\n"));
30
+ end
31
+
32
+ # For dependency documentation see:
33
+ # http://guides.rubygems.org/patterns/
34
+
35
+ Hoe.spec 'cook' do
36
+ developer('Stephen Gaito', 'stephen@perceptisys.co.uk')
37
+ dependency('rake', '~> 0.9.2');
38
+ dependency('construct', '~> 0.1.7');
39
+ dependency('erubis', '~> 2.7');
40
+ dependency('greenletters', '~> 0.2');
41
+ dependency('gpgme', '~> 2.0');
42
+ dependency('highline', '~> 1.6.1');
43
+ end
44
+
45
+ # vim: syntax=ruby
data/bin/cook ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ #
5
+ # Copyright (c) 2014 Stephen Gaito
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # SOFTWARE.
26
+ #
27
+ # ++
28
+
29
+ begin
30
+ require 'rubygems';
31
+ gem 'rake';
32
+ rescue LoadError
33
+ end
34
+
35
+ require 'rake'
36
+ require 'rake/extensions.rb'; # provides mesg used by others
37
+ require 'rake/config.rb';
38
+ require 'rake/cookUtils.rb';
39
+
40
+ # simulate the --trace option
41
+ Rake.application.options.trace = true;
42
+ Rake.verbose(true);
43
+
44
+ Rake.application.run
data/bin/cookDecrypt ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ #
5
+ # Copyright (c) 2014 Stephen Gaito
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # SOFTWARE.
26
+ #
27
+ # ++
28
+
29
+
30
+ # based on ideas taken from:
31
+ # Encrypting and decrypting some data
32
+ # at: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html
33
+ # see also:
34
+ # http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKCS5.html
35
+
36
+ require 'rake'
37
+ require 'rake/extensions.rb'; # provides mesg used by others
38
+ require 'rake/config.rb';
39
+ require 'rake/cookUtils';
40
+
41
+ if ARGV.length < 1 then
42
+ puts "usage: cookDecrypt <<cypher text file to decrypt>>.enc";
43
+ exit(-1);
44
+ end
45
+
46
+ # check to ensure the file name conforms to our naming conventions...
47
+ #
48
+ if ARGV[0] !~ /\.dec$/ then
49
+ puts "The file to be decrypted (#{ARGV[0]}) MUST have the file extension '.enc'";
50
+ exit(-1);
51
+ end
52
+
53
+ # check to make sure the file exists...
54
+ #
55
+ if !File.exists?(ARGV[0]) then
56
+ puts "The file to be decrypted (#{ARGV[0]}) does not exist";
57
+ exit(-1);
58
+ end
59
+
60
+ # Ensure the configuration has been loaded...
61
+ Rake.application.raw_load_rakefile;
62
+
63
+ decrypted = "";
64
+ begin
65
+ decrypted = gpgDecryptFile2Data(ARGV[0]);
66
+ rescue CookDecryptionError => e
67
+ puts "Failed decryption using GnuPG";
68
+ puts "(#{e.message})...";
69
+ puts "\ntrying OpenSSL...";
70
+ decrypted = openSslDecryptFile2Data(ARGV[0]);
71
+ end
72
+
73
+ File.open(ARGV[0].sub(/\.enc/,'.dec'),'w') do | decryptedFile |
74
+ decryptedFile.puts decrypted;
75
+ end
data/bin/cookEncrypt ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ #
5
+ # Copyright (c) 2014 Stephen Gaito
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # SOFTWARE.
26
+ #
27
+ # ++
28
+
29
+ # based on ideas taken from:
30
+ # Encrypting and decrypting some data
31
+ # at: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html
32
+ # see also:
33
+ # http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKCS5.html
34
+
35
+ require 'rake'
36
+ require 'rake/extensions.rb'; # provides mesg used by others
37
+ require 'rake/config.rb';
38
+ require 'rake/cookUtils';
39
+
40
+ if ARGV.length < 1 then
41
+ puts "usage: cookEncrypt <<plain text file to encrypt>>.dec";
42
+ exit(-1);
43
+ end
44
+
45
+ # check to ensure the file name conforms to our naming conventions...
46
+ #
47
+ if ARGV[0] !~ /\.dec$/ then
48
+ puts "The file to be encrypted (#{ARGV[0]}) MUST have the file extension '.dec'";
49
+ exit(-1);
50
+ end
51
+
52
+ # check to make sure the file exists...
53
+ #
54
+ if !File.exists?(ARGV[0]) then
55
+ puts "The file to be encrypted (#{ARGV[0]}) does not exist";
56
+ exit(-1);
57
+ end
58
+
59
+ # get the plain text...
60
+ #
61
+ plainText = File.open(ARGV[0]).read;
62
+
63
+ # Ensure the configuration has been loaded...
64
+ Rake.application.raw_load_rakefile;
65
+
66
+ # encrypt the file...
67
+ #
68
+ begin
69
+ gpgEncryptData2File(plainText, ARGV[0].sub(/\.dec/,'.enc'));
70
+ rescue CookEncryptionError => e
71
+ puts "Failed to encrypt using GnuPG";
72
+ puts "(#{e.message})...";
73
+ puts "\ntrying OpenSSL...";
74
+ openSslEncryptData2File(plainText, ARGV[0].sub(/\.dec/,'.enc'));
75
+ end
76
+
77
+ ##
78
+ # Returns +true+ if the +program+ executable is found in the user's path.
79
+ # Taken from: http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby/3380168#3380168
80
+ #
81
+ def has_program?(program)
82
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
83
+ File.executable?(File.join(directory, program.to_s))
84
+ end
85
+ end
86
+
87
+ if File.exists?(ARGV[0]) then
88
+ if has_program?('srm') then
89
+ puts "srm (secure remove) may require more entropy... so please move the mouse";
90
+ system("srm -v #{ARGV[0]}");
91
+ else
92
+ puts "if you had 'srm' installed then we could more securely remove the decrypted passwords in the file #{ARGV[0]}";
93
+ system ("rm #{ARGV[0]");
94
+ end
95
+ end
data/lib/cook.rb ADDED
@@ -0,0 +1,3 @@
1
+ class Cook
2
+ VERSION = '2.0.1'
3
+ end
@@ -0,0 +1,642 @@
1
+
2
+ begin
3
+ require 'rubygems';
4
+ gem 'construct';
5
+ rescue LoadError
6
+ end
7
+
8
+ require 'construct';
9
+ require 'yaml';
10
+ require 'fileutils';
11
+ #require 'regexp';
12
+
13
+ class Construct
14
+
15
+ def empty?()
16
+ @data.empty?();
17
+ end
18
+
19
+ def has_key?(key)
20
+ @data.has_key?(key);
21
+ end
22
+
23
+ def each_pair(*args, &block)
24
+ @data.each_pair(*args, &block)
25
+ end
26
+
27
+ def each_key(*args, &block)
28
+ @data.each_key(*args, &block)
29
+ end
30
+
31
+ alias_method :orig_key_value_assignment, :[]= ;
32
+ def []=(key, value)
33
+ value = Hash.new() if value.nil?;
34
+
35
+ if @data.include?(key) && @data[key].is_a?(Construct) && (value.is_a?(Hash) || value.is_a?(Construct)) then
36
+ value = @data[key].merge(value);
37
+ end
38
+ orig_key_value_assignment(key, value);
39
+ end
40
+
41
+ def merge(valueToMerge)
42
+ if valueToMerge.is_a?(Hash) || valueToMerge.is_a?(Construct) then
43
+ valueToMerge.each do | key, value |
44
+ key = key.to_sym;
45
+ if value.is_a?(Hash) || value.is_a?(Construct) then
46
+ self[key] = Construct.new() unless self.has_key?(key);
47
+ if !self[key].is_a?(Construct) then
48
+ raise ArgumentError, "attempting to merge a Hash/Construct into an existing non Hash/Construct key [#{key}]";
49
+ end
50
+ self[key].merge(value);
51
+ elsif value.is_a?(Array) then
52
+ self[key] = Array.new() unless self.has_key?(key);
53
+ if !self[key].is_a?(Array) then
54
+ raise ArgumentError, "attempting to merge an Array into an existing non Array key [#{key}]";
55
+ end
56
+ value.each do | item |
57
+ self[key].push(item);
58
+ end
59
+ else
60
+ self[key] = value;
61
+ end
62
+ end
63
+ end
64
+ if valueToMerge.is_a?(Construct) then
65
+ @schema.merge(valueToMerge.schema);
66
+ end
67
+ return self;
68
+ end
69
+
70
+ def to_stringHash()
71
+ result = Hash.new();
72
+ data.each_pair do | aKey, aValue |
73
+ if aValue.is_a?(Construct) then
74
+ aValue = aValue.to_stringHash();
75
+ end
76
+ result[aKey.to_s] = aValue;
77
+ end
78
+ return result;
79
+ end
80
+
81
+ end
82
+
83
+ class ConfigurationError < StandardError
84
+ end
85
+
86
+ class ResourceNotFoundError < StandardError
87
+ end
88
+
89
+ class Conf
90
+
91
+ @@data = Construct.new();
92
+ @@encryptedData = Construct.new();
93
+ @@passPhrase = nil;
94
+ @@configFileNames = Array.new();
95
+ @@recipePaths = Array.new();
96
+
97
+ def self.get_pass_phrase(check = false)
98
+ if @@passPhrase.nil? then
99
+ require 'highline';
100
+ @@passPhrase = HighLine.new.ask("Pass phrase for encrypted configuration:") { | q | q.echo='*'; }
101
+ while check do
102
+ secondPassPhrase = HighLine.new.ask("Pass phrase (again):") { | q | q.echo='*'; }
103
+ if @@passPhrase != secondPassPhrase then
104
+ puts "The pass phrases do not match... please try again\n\n";
105
+ @@passPhrase = HighLine.new.ask("Pass phrase for encrypted configuration:") { | q | q.echo='*'; }
106
+ else
107
+ check = false;
108
+ end
109
+ end
110
+ end
111
+ @@passPhrase;
112
+ end
113
+
114
+ def self.data
115
+ return @@data;
116
+ end
117
+
118
+ def self.encryptedData
119
+ return @@encryptedData;
120
+ end
121
+
122
+ def self.has_key?(aKey)
123
+ return @@data.has_key?(aKey);
124
+ end
125
+
126
+ def self.has_encrypted_key?(aKey)
127
+ return @@encryptedData.has_key?(aKey);
128
+ end
129
+
130
+ def self.load(yaml)
131
+ loadedHash = YAML::load(yaml);
132
+ if loadedHash then
133
+ @@data.merge(loadedHash);
134
+ end
135
+ end
136
+
137
+ def self.load_file(yamlFileName)
138
+ Rake::Application.mesg "loading configuration file [#{yamlFileName}]\n in #{Dir.getwd}" if Rake.application.options.trace;
139
+ loadedHash = YAML::load_file(yamlFileName);
140
+ if loadedHash then
141
+ @@data.merge(loadedHash);
142
+ end
143
+ end
144
+
145
+ def self.load_encrypted_file(yamlFileName)
146
+ Rake::Application.mesg "loading encrypted configuration file [#{yamlFileName}]\n in #{Dir.getwd}" if Rake.application.options.trace;
147
+ yamlPlainText = "";
148
+ begin
149
+ yamlPlainText = gpgDecryptFile2Data(yamlFileName);
150
+ rescue
151
+ yamlPlainText = openSslDecryptFile2Data(yamlFileName);
152
+ end
153
+ loadedHash = YAML::load(yamlPlainText);
154
+ if loadedHash then
155
+ @@encryptedData.merge(loadedHash);
156
+ end
157
+ end
158
+
159
+ def self.save_file(yamlFileName, branch = [])
160
+ branchData = @@data;
161
+ branch.each do | aKey |
162
+ branchData = branchData[aKey] if branchData.has_key?(aKey);
163
+ end
164
+ branchData = branchData.to_stringHash if branchData.is_a?(Construct);
165
+ branch.reverse.each do | aKey |
166
+ tmpHash = Hash.new();
167
+ tmpHash[aKey.to_s] = branchData;
168
+ branchData = tmpHash;
169
+ end
170
+ yamlFile = File.open(yamlFileName, "w");
171
+ YAML::dump(branchData, yamlFile);
172
+ yamlFile.close();
173
+ end
174
+
175
+ def self.method_missing(meth, *args)
176
+ meth_s = meth.to_s
177
+ if @@data.respond_to?(meth) ||
178
+ @@data.data.has_key?(meth) ||
179
+ @@data.schema.has_key?(meth) ||
180
+ meth_s[-1..-1] == '='then
181
+ @@data.method_missing(meth, *args);
182
+ else
183
+ raise ConfigurationError, "No configuation value specified for Conf[#{meth.to_s}]";
184
+ end
185
+ end
186
+
187
+ def self.add_config_file_name(configFileName)
188
+ configFileName.sub!(/\.[^\.]*$/,'');
189
+ Rake::Application.mesg "Adding config name: [#{configFileName}]";
190
+ @@configFileNames.push(configFileName);
191
+ end
192
+
193
+ def self.load_config_files(recipeDir)
194
+ @@configFileNames.each() do | aConfigFileName |
195
+ #
196
+ # load the yaml configuration file
197
+ #
198
+ confFile = recipeDir + '/' + aConfigFileName + '.conf';
199
+ self.load_file(confFile) if File.exists?(confFile);
200
+ encConfFile = recipeDir + '/' + aConfigFileName + '.enc';
201
+ self.load_encrypted_file(encConfFile) if File.exists?(encConfFile);
202
+ end
203
+ end
204
+
205
+ def self.load_rake_files(recipeDir)
206
+ @@configFileNames.each() do | aConfigFileName |
207
+ #
208
+ # load the corresponding rake file
209
+ #
210
+ rakeFile = recipeDir + '/' + aConfigFileName + '.rake';
211
+ Rake.load_rakefile(rakeFile) if File.exists?(rakeFile);
212
+ end
213
+ end
214
+
215
+ def self.add_recipes_path(aRecipesPath)
216
+ @@recipePaths.push(aRecipesPath);
217
+ end
218
+
219
+ def self.get_recipe_paths()
220
+ @@recipePaths;
221
+ end
222
+
223
+ def self.find_resource(aPartialResourcePath)
224
+ @@recipePaths.each do | aRecipePath |
225
+ aResourcePath = aRecipePath+aPartialResourcePath;
226
+ return aResourcePath if File.exists?(aResourcePath);
227
+ end
228
+ raise ResourceNotFoundError, "Resource file #{aPartialResourcePath} could not be found";
229
+ end
230
+
231
+ def self.each_resource(aPartialResourcePath, &aBlock)
232
+ @@recipePaths.each do | aRecipePath |
233
+ aResourcePath = aRecipePath+aPartialResourcePath;
234
+ next unless File.exists?(aResourcePath);
235
+ aBlock.call(aResourcePath);
236
+ end
237
+ end
238
+
239
+ def self.add_cookbook(aCookbookPath)
240
+ $LOAD_PATH.unshift(aCookbookPath+'/lib');
241
+ Conf.add_recipes_path(aCookbookPath+'/');
242
+ Conf.add_recipes_path(aCookbookPath+'/recipes/');
243
+ # Dir.chdir(aCookbookPath) do
244
+ #
245
+ # load the cookbook configuration file first incase they are used
246
+ # in the rakefile
247
+ confFile = aCookbookPath + '/' + 'cookbook.conf';
248
+ Conf.load_file(confFile) if File.exists?(confFile);
249
+ encConfFile = aCookbookPath + '/' + 'cookbook.enc';
250
+ Conf.load_encrypted_file(encConfFile) if File.exists?(encConfFile);
251
+
252
+ # now load the corresponding cookbook rake file
253
+ rakeFile = aCookbookPath + '/' + 'cookbook.rake';
254
+ Rake.load_rakefile(rakeFile) if File.exists?(rakeFile);
255
+ # end
256
+ end
257
+
258
+ def self.log_file_name()
259
+ return @@configFileNames.join('-') unless @@configFileNames.empty?;
260
+ return "noConfig";
261
+ end
262
+
263
+ def self.load_recipe_dir(recipeDir)
264
+ #
265
+ # extract the last directory name in the path to use as the
266
+ # recipeName
267
+ #
268
+ recipeName = recipeDir.split(/\//).pop();
269
+ #
270
+ # search for sub-directories and recursively calling
271
+ # load_recipe_dir to load any *.conf/*.enc or *.rake fragments
272
+ # found.
273
+ #
274
+ Dir.glob(recipeDir+'/*').each do | aFileName |
275
+ next unless aFileName.dup.force_encoding("UTF-8").valid_encoding?
276
+ next if aFileName =~ /\.$/;
277
+ next unless File.directory?(aFileName);
278
+ load_recipe_dir(aFileName);
279
+ end
280
+ #
281
+ # load the configurtion files first in case they are used in the
282
+ # rakefile
283
+ # ... generic first ...
284
+ confFile = recipeDir + '/' + recipeName + '.conf';
285
+ Conf.load_file(confFile) if File.exists?(confFile);
286
+ encConfFile = recipeDir + '/' + recipeName + '.enc';
287
+ Conf.load_encrypted_file(encConfFile) if File.exists?(encConfFile);
288
+ # ... then more specific ...
289
+ Conf.load_config_files(recipeDir);
290
+
291
+ # now load the corresponding rake files
292
+ # ... generic first...
293
+ rakeFile = recipeDir + '/' + recipeName + '.rake';
294
+ Rake.load_rakefile(rakeFile) if File.exists?(rakeFile);
295
+ # ... then more specific ...
296
+ Conf.load_rake_files(recipeDir);
297
+ end
298
+
299
+ def self.load_recipe(recipeName)
300
+ @@recipePaths.reverse.each do | aRecipePath |
301
+ recipeDir = aRecipePath+recipeName;
302
+ next unless File.directory?(recipeDir);
303
+ load_recipe_dir(recipeDir);
304
+ end
305
+ end
306
+
307
+ def self.load_central_config_files()
308
+ @@recipePaths.reverse.each do | aRecipePath |
309
+ load_config_files(aRecipePath.sub(/\/$/,''));
310
+ end
311
+ end
312
+
313
+ end
314
+
315
+ module Rake
316
+
317
+ class <<self
318
+ alias_method :rake_config_original_load_rakefile, :load_rakefile
319
+ def load_rakefile(*args)
320
+ Rake::Application.mesg "Loading rakefile [#{args[0]}]\n in #{Dir.getwd}" if Rake.application.options.trace;
321
+ rake_config_original_load_rakefile(*args);
322
+ end
323
+ end
324
+
325
+ class Application
326
+
327
+ alias_method :rake_config_original_standard_rake_options, :standard_rake_options ;
328
+ def standard_rake_options
329
+ options = rake_config_original_standard_rake_options();
330
+ options.push(
331
+ ['--config', '-c YAML-CONFIG-FILE-NAME', "Load YAML configuration files named YAML-CONFIG-FILE-NAME in each receipe if it exists",
332
+ lambda { |value|
333
+ confValues = value.split(/,/).each do | aValue |
334
+ Conf.add_config_file_name(aValue);
335
+ end
336
+ }
337
+ ]);
338
+ return options;
339
+ end
340
+
341
+ alias_method :rake_config_original_collect_tasks, :collect_tasks
342
+ def collect_tasks
343
+ rake_config_original_collect_tasks;
344
+ @top_level_tasks.unshift("cookPostConfig");
345
+ @top_level_tasks.unshift("cookConfig");
346
+ @top_level_tasks.unshift("cookPreConfig");
347
+ end
348
+
349
+ alias_method :rake_config_original_top_level, :top_level
350
+ def top_level
351
+ # load the central config files after all recipes
352
+ Conf.load_central_config_files();
353
+ # but before we start invoking tasks
354
+ rake_config_original_top_level;
355
+ end
356
+
357
+ def raw_load_rakefile
358
+ myCookbookDir = File.expand_path(Dir.getwd());
359
+ FileUtils.mkdir_p('logs');
360
+ Rake::Application.openLogger('logs/'+Conf.log_file_name.gsub(/\//,'_')+'-buildLog');
361
+ Rake::Application.logger.level = Logger::DEBUG;
362
+ Rake::Application.mesg "Building in #{myCookbookDir}";
363
+ Conf.add_cookbook(myCookbookDir);
364
+ end
365
+
366
+ end
367
+
368
+ class Task
369
+
370
+ # Execute the rsync command on the arguments provided using greenletters.
371
+ def self.scp_to_remote_scp(localPath, remotePath, options = {}, &aBlock)
372
+ options = Conf.scp.data.merge(options);
373
+ options[:timeout] = 10 unless options.has_key?(:timeout);
374
+ cmd = [options[:command],
375
+ options[:cmdOptions],
376
+ localPath,
377
+ "#{options[:userId]}@#{options[:remoteHost]}:#{remotePath}",
378
+ ].flatten
379
+
380
+ gl_run(*cmd, options, &aBlock);
381
+ end
382
+
383
+ # Execute the rsync command on the arguments provided using greenletters.
384
+ def self.scp_from_remote(remotePath, localPath, options = {}, &aBlock)
385
+ options = Conf.scp.data.merge(options);
386
+ options[:timeout] = 10 unless options.has_key?(:timeout);
387
+ cmd = [options[:command],
388
+ options[:cmdOptions],
389
+ "#{options[:userId]}@#{options[:remoteHost]}:#{remotePath}",
390
+ localPath,
391
+ ].flatten
392
+
393
+ gl_run(*cmd, options, &aBlock);
394
+ end
395
+
396
+ # Execute the rsync command on the arguments provided using greenletters.
397
+ def self.rsync_to_remote(localPath, remotePath, options = {}, &aBlock)
398
+ if localPath =~ /\*/ then
399
+ localPath = Dir.glob(localPath);
400
+ end
401
+ options = Conf.rsync.data.merge(options);
402
+ options[:timeout] = 10 unless options.has_key?(:timeout);
403
+ cmd = [options[:command],
404
+ options[:cmdOptions],
405
+ localPath,
406
+ "#{options[:userId]}@#{options[:remoteHost]}:#{remotePath}",
407
+ ].flatten
408
+
409
+ gl_run(*cmd, options, &aBlock);
410
+ end
411
+
412
+ # Execute the rsync command on the arguments provided using greenletters.
413
+ def self.rsync_from_remote(remotePath, localPath, options = {}, &aBlock)
414
+ options = Conf.rsync.data.merge(options);
415
+ options[:timeout] = 10 unless options.has_key?(:timeout);
416
+ cmd = [options[:command],
417
+ options[:cmdOptions],
418
+ "#{options[:userId]}@#{options[:remoteHost]}:#{remotePath}",
419
+ localPath,
420
+ ].flatten
421
+
422
+ gl_run(*cmd, options, &aBlock);
423
+ end
424
+
425
+ # Execute +command+ using ssh to remote_host using greenletters.
426
+ def self.remote_ssh(*args, &aBlock)
427
+ options = if args.last.is_a?(Hash) then args.pop else {} end
428
+ options = Conf.ssh.data.merge(options);
429
+ options[:timeout] = 10 unless options.has_key?(:timeout);
430
+ sshPort = Array.new();
431
+ sshPort = [ '-p', options[:remotePort]] if options.has_key?(:remotePort);
432
+ cmd = [options[:command],
433
+ options[:cmdOptions],
434
+ '-l',
435
+ options[:userId],
436
+ sshPort,
437
+ options[:remoteHost]
438
+ ].flatten
439
+
440
+ gl_run(*cmd, options) do | p |
441
+ p.wait_for(:output, Regexp.new(options[:commandPromptRegExp]));
442
+ Rake::Application.mesg args.flatten.join(' ') if Rake.application.options.trace;
443
+ p.puts args.flatten.join(' ') + '; exit';
444
+ end
445
+ end
446
+
447
+ # Execute +command+ using sh on local_host using greenletters.
448
+ def self.local_sh(*args, &aBlock)
449
+ options = if args.last.is_a?(Hash) then args.pop else {} end
450
+ options = Conf.sh.data.merge(options);
451
+ cmd = [options[:command],
452
+ options[:cmdOptions],
453
+ "-c",
454
+ args.flatten.join(' ')
455
+ ].flatten
456
+
457
+ gl_run(*cmd, options, &aBlock);
458
+ end
459
+
460
+ # Execute +command+ (as is) on local_host using greenletters to
461
+ # enable expect like behaviour (useful for dealling with
462
+ # passwords).
463
+ def self.gl_run(*args, &aBlock)
464
+ $TRACE = Rake.application.options.trace;
465
+
466
+ options = Hash.new;
467
+ if args.last.is_a?(Construct) then
468
+ raise ConfigurationError, "gl_run should not be passed a Construct";
469
+ elsif args.last.is_a?(Hash)
470
+ options = args.pop;
471
+ end
472
+ Conf.timeout = Hash.new() unless Conf.has_key?(:timeout);
473
+ Conf.timeout.maxTimeout = 10 unless Conf.timeout.has_key?(:maxTimeout);
474
+ if options.has_key?(:timeout) then
475
+ options[:timeout] = Conf.timeout.maxTimeout if Conf.timeout.maxTimeout.to_i < options[:timeout].to_i ;
476
+ else
477
+ options[:timeout] = 10;
478
+ end
479
+
480
+ Rake::Application.mesg args.join(' ') if $TRACE
481
+ Rake::Application.mesg "#{options}" if $TRACE && !options.empty?() && options.has_key?(:verbose);
482
+
483
+ asyncTriggersBlocks = Array.new;
484
+ if options.has_key? :asyncTriggersBlocks then
485
+ asyncTriggersBlocks = options[:asyncTriggersBlocks];
486
+ end
487
+
488
+ exitStatus = 0;
489
+ if options.has_key? :exitStatus then
490
+ exitStatus = options[:exitStatus];
491
+ end
492
+
493
+ options[:env] = ENV unless options.has_key?(:env);
494
+ options[:transcript] = Rake::Application.logger unless options.has_key?(:transcript);
495
+
496
+ cmdProcess = Greenletters::Process.new(*args, options);
497
+ cmdProcess.start!;
498
+
499
+ asyncTriggersBlocks.each do | asyncTriggersBlock |
500
+ if asyncTriggersBlock.kind_of? Proc then
501
+ asyncTriggersBlock.call(cmdProcess);
502
+ end
503
+ end
504
+
505
+ aBlock.call(cmdProcess) unless aBlock.nil?;
506
+
507
+ cmdProcess.wait_for(:exit, exitStatus);
508
+
509
+ cmdProcess
510
+ end
511
+
512
+ end
513
+
514
+ module DSL
515
+
516
+ def config(*args, &block)
517
+ Rake::Task.define_task(*args, &block);
518
+ end
519
+
520
+ def load_recipe(recipeName)
521
+ Conf.load_recipe(recipeName);
522
+ end
523
+
524
+ def add_cookbook(aCookbookPath)
525
+ Conf.add_cookbook(aCookbookPath);
526
+ end
527
+
528
+ def find_resource(aPartialResourcePath)
529
+ Conf.find_resource(aPartialResourcePath);
530
+ end
531
+
532
+ def read_resource(aPartialResourcePath)
533
+ File.open(Conf.find_resource(aPartialResourcePath)).read;
534
+ end
535
+
536
+ def each_resource(aPartialResourcePath, &aBlock)
537
+ Conf.each_resource(aPartialResourcePath, &aBlock);
538
+ end
539
+
540
+ def set_command_options(commandSymbols, options = {}, &asyncTriggersBlock)
541
+ commandSymbols = [ :local, :remote ] if commandSymbols == :all;
542
+ commandSymbols = [ :local, :remote ] if commandSymbols.kind_of? Array and commandSymbols.include? :all;
543
+ commandSymbols = [ commandSymbols ] unless commandSymbols.kind_of? Array;
544
+ commandSymbols.push(:sh).flatten! if commandSymbols.include?(:local);
545
+ commandSymbols.push([:scp, :ssh, :rsync]).flatten! if commandSymbols.include?(:remote);
546
+ commandSymbols.each do | aCommandSymbol |
547
+ aCommandSymbol = aCommandSymbol.to_sym;
548
+ next if aCommandSymbol == :all;
549
+ next if aCommandSymbol == :local;
550
+ next if aCommandSymbol == :remote;
551
+ if !Conf.data.has_key?(aCommandSymbol) then
552
+ Conf.data[aCommandSymbol] = Hash.new;
553
+ Conf.data[aCommandSymbol].command = aCommandSymbol.to_s;
554
+ Conf.data[aCommandSymbol].cmdOptions = Array.new;
555
+ Conf.data[aCommandSymbol].commandPromptRegExp = '.';
556
+ Conf.data[aCommandSymbol].asyncTriggersBlocks = Array.new;
557
+ end
558
+ Conf.data[aCommandSymbol].merge(options);
559
+ Conf.data[aCommandSymbol].asyncTriggersBlocks.push(asyncTriggersBlock);
560
+ end
561
+ end
562
+
563
+ def local_sh(*args, &block)
564
+ Rake::Task.local_sh(*args, &block);
565
+ end
566
+
567
+ def local_run(*args, &block)
568
+ Rake::Application.mesg "Use of local_run is depreciated. Please use local_sh instead";
569
+ Rake::Task.local_sh(*args, &block);
570
+ end
571
+
572
+ def remote_ssh(*args, &block)
573
+ Rake::Task.remote_ssh(*args, &block);
574
+ end
575
+
576
+ def remote_run(*args, &block)
577
+ Rake::Application.mesg "Use of remote_run is depreciated. Please use remote_ssh instead";
578
+ Rake::Task.remote_ssh(*args, &block);
579
+ end
580
+
581
+ def scp_to_remote(localPath, remotePath, options = {}, &block)
582
+ Rake::Task.scp_to_remote(localPath, remotePath, options &block);
583
+ end
584
+
585
+ def scp_from_remote(remotePath, localPath, options = {}, &block)
586
+ Rake::Task.scp_from_remote(remotePath, localPath, options, &block);
587
+ end
588
+
589
+ def rsync_to_remote(localPath, remotePath, options = {}, &aBlock)
590
+ Rake::Task.rsync_to_remote(localPath, remotePath, options, &aBlock);
591
+ end
592
+
593
+ def rsync_from_remote(remotePath, localPath, options = {}, &aBlock)
594
+ Rake::Task.rsync_from_remote(remotePath, localPath, options, &aBlock);
595
+ end
596
+
597
+ end
598
+
599
+ end
600
+
601
+ self.extend Rake::DSL
602
+
603
+
604
+ # Ensure the configuration for the sh, scp, ssh and rsync commands are setup
605
+ #Conf.sh = Hash.new;
606
+ #Conf.sh.command = "sh";
607
+ #Conf.sh.cmdOptions = Array.new;
608
+ #Conf.ssh = Hash.new;
609
+ #Conf.ssh.command = "ssh";
610
+ #Conf.ssh.cmdOptions = Array.new;
611
+ #Conf.scp = Hash.new;
612
+ #Conf.scp.command = "scp";
613
+ #Conf.scp.cmdOptions = Array.new;
614
+ #Conf.rsync = Hash.new;
615
+ #Conf.rsync.command = "rsync";
616
+ #Conf.rsync.cmdOptions = Array.new;
617
+ set_command_options(:all);
618
+
619
+ # Ensure the gpg hash exists (but may be empty)
620
+ Conf.gpg = Hash.new;
621
+
622
+ $LOAD_PATH.push('.');
623
+
624
+ config :cookPreConfig do
625
+ Rake::Application.mesg "finished (cook) preConfig stage\n\n";
626
+ end
627
+
628
+ config :cookConfig do
629
+ Rake::Application.mesg "finished (cook) config stage\n\n";
630
+ end
631
+
632
+ config :cookPostConfig do
633
+ Rake::Application.mesg "finished (cook) postConfig stage\n\n";
634
+ if Rake.application.options.trace then
635
+ Rake::Application.mesg "--------------------\nTasks defined\n\n";
636
+ Rake::Application.mesg_pp Rake.application.tasks;
637
+ Rake::Application.mesg "--------------------\nConfiguration data\n\n";
638
+ Rake::Application.mesg_pp Conf.data;
639
+ Rake::Application.mesg "--------------------\n\n";
640
+ end
641
+ end
642
+