taste_tester 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 13e9b1fb00c7044fe801ccb1158bbaacbbe30dd9
4
- data.tar.gz: 3fcd362ea2bbdfed358847a485a695c9669d44df
2
+ SHA256:
3
+ metadata.gz: dbe88f9386cbb7c68b721ea057a30881dd98ba374a0e84528e43da739eb88d31
4
+ data.tar.gz: 642d43ce2aa006331680178e0ab15afba8d88323ab89d2e1cb0321f74c663b07
5
5
  SHA512:
6
- metadata.gz: f4da6aa14ee5b8c3f732eae1480f9793318b377f1574bf1bb12f2ec8b39c91edd8e8635d9ebd6e9042bf62a767ae55655ee447b0b47bfb83fbba4d1b13832e66
7
- data.tar.gz: 67db565bdee6e84b0ad23c73183f88af0873ac12a84ae1451c8587038222634e0bf6888ef1e9a30280631586ad7a350087ad7a2f74f249f3a324f208a3a2395a
6
+ metadata.gz: 3a256a6d7e67b346aad620a1782032394f68848ef9c085c24854b4d70ffdd718b64d4970c479b7080aba11b468423905759d0ae8f92d305aa38fe654187745b7
7
+ data.tar.gz: '048097e573d03b748a08d09438820d9ee144c90d0fb43e0375fbd91e1354215d7c82d8a0b39f97452b0752ab8dc79fe5b5863c9108727ba87fc2810271f27ee8'
data/README.md CHANGED
@@ -37,13 +37,13 @@ taste-tester untest [host] # Put host back in production
37
37
  # (optional - will revert itself after 1 hour)
38
38
  ```
39
39
 
40
- See the help for futher information.
40
+ See the help for further information.
41
41
 
42
42
  ## Prerequisites
43
43
 
44
- * Taste Tester assumes that /etc/chef/client.rb and /etc/chef/client.pem on your
45
- servers is a symlink and that your real config is /etc/chef/client-prod.rb and
46
- /etc/chef/client-prod.pem, respectively.
44
+ * Taste Tester assumes that `/etc/chef/client.rb` and `/etc/chef/client.pem` on your
45
+ servers is a symlink and that your real config is `/etc/chef/client-prod.rb` and
46
+ `/etc/chef/client-prod.pem`, respectively.
47
47
 
48
48
  * Taste Tester assumes that it's generally safe to "go back" to production. I.e.
49
49
  We set things up so you can set a cronjob to un-taste-test a server after the
@@ -61,6 +61,7 @@ you want to test on, i.e. SSH public/private keys, SSH certificates, Kerberos
61
61
  * Mixlib::Config
62
62
  * Colorize
63
63
  * BetweenMeals
64
+ * Minitar
64
65
 
65
66
  ## Automatic Untesting
66
67
 
@@ -79,15 +80,15 @@ is also killing the ssh tunnel whose PID is in `/etc/chef/test_timestamp`
79
80
  ## Config file
80
81
 
81
82
  The default config file is `/etc/taste-tester-config.rb` but you may use -c to
82
- specify another. The config file works the same as client.rb does for Chef -
83
- there are a series of keywords that take an arguement and anything else is just
83
+ specify another. The config file works the same as `client.rb` does for Chef -
84
+ there are a series of keywords that take an argument and anything else is just
84
85
  standard Ruby.
85
86
 
86
87
  All command-line options are available in the config file:
87
88
  * debug (bool, default: `false`)
88
89
  * timestamp (bool, default: `false`)
89
90
  * config_file (string, default: `/etc/taste-tester-config.rb`)
90
- * plugin_path (string, default: `/etc/taste-tester-plugin.rb`)
91
+ * plugin_path (string, no default)
91
92
  * repo (string, default: `#{ENV['HOME']}/ops`)
92
93
  * testing_time (int, default: `3600`)
93
94
  * chef_client_command (strng, default: `chef-client`)
@@ -110,6 +111,10 @@ The following options are also available:
110
111
  `#{ENV['HOME']}/.chef/taste-tester-ref.txt`
111
112
  * checksum_dir - The checksum directory to put in knife.conf for users. Default:
112
113
  `#{ENV['HOME']}/.chef/checksums`
114
+ * bundle - use a single tar.gz file for transporting cookbooks, roles and
115
+ databags to clients. Experimental.
116
+ Default: false
117
+
113
118
 
114
119
  ## Plugin
115
120
 
@@ -150,3 +155,24 @@ Stuff to do after putting all hosts in test mode.
150
155
  * self.repo_checks(dryrun, repo)
151
156
 
152
157
  Additional checks you want to do on the repo as sanity checks.
158
+
159
+ **Plugin example**
160
+
161
+ This is an example `/etc/taste-tester-plugin.rb` to add a user-defined string
162
+ to `client-taste-tester.rb` on the remote system:
163
+ ```
164
+ Hooks.class_eval do
165
+ def self.test_remote_client_rb_extra_code(_hostname)
166
+ %(
167
+ # This comment gets added to client-taste-tester.rb
168
+ # This one too.
169
+ )
170
+ end
171
+ end
172
+ ```
173
+ Be sure to pass this plugin file with `-p` on the command line or set it as
174
+ `plugin_path` in your `taste-tester-config.rb` file.
175
+
176
+ ## License
177
+
178
+ See the `LICENSE` file.
data/bin/taste-tester CHANGED
@@ -28,17 +28,17 @@ require 'taste_tester/commands'
28
28
  require 'taste_tester/hooks'
29
29
  require 'taste_tester/exceptions'
30
30
 
31
- include TasteTester::Logging
32
-
33
- if ENV['USER'] == 'root'
34
- logger.warn('You should not be running as root')
35
- exit(1)
36
- end
37
-
38
31
  # Command line parsing and param descriptions
39
32
  module TasteTester
33
+ extend TasteTester::Logging
34
+
40
35
  verify = 'Verify your changes were actually applied as intended!'.red
41
36
 
37
+ if ENV['USER'] == 'root'
38
+ logger.warn('You should not be running as root')
39
+ exit(1)
40
+ end
41
+
42
42
  # Do an initial read of the config file if it's in the default place, so
43
43
  # that if people override chef_client_command the help message is correct.
44
44
  if File.exists?(File.expand_path(TasteTester::Config.config_file))
@@ -48,7 +48,7 @@ module TasteTester
48
48
  end
49
49
 
50
50
  cmd = TasteTester::Config.chef_client_command
51
- description = <<-EOF
51
+ description = <<-ENDOFDESCRIPTION
52
52
  Welcome to taste-tester!
53
53
 
54
54
  Usage: taste-tester <mode> [<options>]
@@ -74,6 +74,13 @@ MODES:
74
74
  point some production server specified by -s to your virtual chef server for
75
75
  testing. If you have you a plugin that uses the hookpoint, it'll may amend
76
76
  your commit message to denote the server you tested.
77
+ Returns:
78
+ 0 Success.
79
+ 1 Failure, e.g. host not reachable or usage error.
80
+ 2 Partial success, mixture of success and failure due to hosts being taste
81
+ tested by other users.
82
+ 3 All hosts were not taste-tested because they are being taste-tested by
83
+ other users.
77
84
 
78
85
  upload
79
86
  Sync your local repo to your virtual Chef server (i.e. just the first step
@@ -83,6 +90,13 @@ MODES:
83
90
  cookbooks and roles. It also does a fair amount of sanity checking on
84
91
  your repo and you may specify --skip-repo-checks to bypass this.
85
92
 
93
+ impact
94
+ Examine local repo changes to determine which roles are potentially
95
+ impacted by the changes. Compares the set of modified cookbooks with the
96
+ dependency lists of each role and reports any role which depends on a
97
+ modified cookbook. It is recommended to test your changes on at least one
98
+ server of each role, potentially also on multiple platforms.
99
+
86
100
  keeptesting
87
101
  Extend the testing time on server specified by -s by 1 hour unless
88
102
  otherwise specified by -t.
@@ -111,7 +125,7 @@ MODES:
111
125
  You probably don't want this. It will restart up the chef-zero server on
112
126
  your localhost. taste-tester dynamically starts this if it's down, so there
113
127
  should be no need to do this manually.
114
- EOF
128
+ ENDOFDESCRIPTION
115
129
 
116
130
  mode = ARGV.shift unless !ARGV.empty? && ARGV[0].start_with?('-')
117
131
 
@@ -241,7 +255,7 @@ MODES:
241
255
  opts.on(
242
256
  '-t', '--testing-time TIME',
243
257
  'How long should the host remain in testing.' +
244
- ' Takes a simple relative time string, such as "45m", "4h" or "2d".'
258
+ ' Takes a simple relative time string, such as "45m", "4h" or "1d".'
245
259
  ) do |time|
246
260
  m = time.match(/^(\d+)([d|h|m]+)$/)
247
261
  if m
@@ -260,7 +274,7 @@ MODES:
260
274
 
261
275
  opts.on(
262
276
  '-r', '--repo DIR',
263
- "Custom repo location, current deafult is #{TasteTester::Config.repo}." +
277
+ "Custom repo location, current default is #{TasteTester::Config.repo}." +
264
278
  ' Works on upload and test.'
265
279
  ) do |dir|
266
280
  options[:repo] = dir
@@ -273,6 +287,16 @@ MODES:
273
287
  options[:repo_type] = type
274
288
  end
275
289
 
290
+ opts.on(
291
+ '--clowntown-no-repo', 'This option enables taste-tester to run ' +
292
+ 'without a SCM repo of any sort. This has negative implications like ' +
293
+ 'always doing a full upload. This option is here for weird corner ' +
294
+ 'cases like having to sync out the export of a repo, but is not ' +
295
+ 'generally a good idea or well supported. Use at your own risk.'
296
+ ) do
297
+ options[:no_repo] = true
298
+ end
299
+
276
300
  opts.on(
277
301
  '-R', '--roles ROLE[,ROLE]', Array,
278
302
  'Specific roles to upload. Intended mostly for debugging,' +
@@ -311,6 +335,12 @@ MODES:
311
335
  options[:ssh_command] = c
312
336
  end
313
337
 
338
+ opts.on(
339
+ '--ssh-connect-timeout TIMEOUT', 'SSH \'-o ConnectTimeout\' value'
340
+ ) do |c|
341
+ options[:ssh_connect_timeout] = c
342
+ end
343
+
314
344
  opts.on('--skip-repo-checks', 'Skip repository sanity checks') do
315
345
  options[:skip_repo_checks] = true
316
346
  end
@@ -319,6 +349,13 @@ MODES:
319
349
  options[:yes] = true
320
350
  end
321
351
 
352
+ opts.on(
353
+ '--json', 'Format output as JSON for programatic consumption.' +
354
+ ' Default to false. Works on impact.'
355
+ ) do
356
+ options[:json] = true
357
+ end
358
+
322
359
  opts.separator ''
323
360
  opts.separator 'Control local hook behavior with these options:'
324
361
 
@@ -397,6 +434,8 @@ MODES:
397
434
  TasteTester::Commands.runchef
398
435
  when :upload
399
436
  TasteTester::Commands.upload
437
+ when :impact
438
+ TasteTester::Commands.impact
400
439
  else
401
440
  logger.error("Invalid mode: #{mode}")
402
441
  puts parser
@@ -14,6 +14,8 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ require 'minitar'
18
+ require 'find'
17
19
  require 'taste_tester/logging'
18
20
  require 'between_meals/repo'
19
21
  require 'between_meals/knife'
@@ -44,16 +46,22 @@ module TasteTester
44
46
  :databag_dir => TasteTester::Config.databags,
45
47
  :checksum_dir => TasteTester::Config.checksum_dir,
46
48
  :role_type => TasteTester::Config.role_type,
49
+ :config => TasteTester::Config.knife_config,
47
50
  )
48
51
  @knife.write_user_config
49
- @repo = BetweenMeals::Repo.get(
50
- TasteTester::Config.repo_type,
51
- TasteTester::Config.repo,
52
- logger,
53
- )
54
- unless @repo.exists?
52
+ if TasteTester::Config.no_repo
53
+ @repo = nil
54
+ else
55
+ @repo = BetweenMeals::Repo.get(
56
+ TasteTester::Config.repo_type,
57
+ TasteTester::Config.repo,
58
+ logger,
59
+ )
60
+ end
61
+ if @repo && !@repo.exists?
55
62
  fail "Could not open repo from #{TasteTester::Config.repo}"
56
63
  end
64
+
57
65
  @track_symlinks = TasteTester::Config.track_symlinks
58
66
  end
59
67
 
@@ -64,26 +72,30 @@ module TasteTester
64
72
  end
65
73
 
66
74
  def upload
67
- checks unless @skip_checks
68
-
69
- logger.info("Last commit: #{@repo.head_rev} " +
70
- "'#{@repo.last_msg.split("\n").first}'" +
71
- " by #{@repo.last_author[:email]}")
75
+ head_rev = nil
76
+ if @repo
77
+ head_rev = @repo.head_rev
78
+ checks unless @skip_checks
79
+ logger.info("Last commit: #{head_rev} " +
80
+ "'#{@repo.last_msg.split("\n").first}'" +
81
+ " by #{@repo.last_author[:email]}")
82
+ end
72
83
 
73
- if @force || !@server.latest_uploaded_ref
84
+ if @force || !@server.latest_uploaded_ref || !@repo
74
85
  logger.info('Full upload forced') if @force
86
+ logger.info('No repo, doing full upload') unless @repo
75
87
  unless TasteTester::Config.skip_pre_upload_hook
76
88
  TasteTester::Hooks.pre_upload(TasteTester::Config.dryrun,
77
89
  @repo,
78
90
  nil,
79
- @repo.head_rev)
91
+ head_rev)
80
92
  end
81
93
  time(logger) { full }
82
94
  unless TasteTester::Config.skip_post_upload_hook
83
95
  TasteTester::Hooks.post_upload(TasteTester::Config.dryrun,
84
96
  @repo,
85
97
  nil,
86
- @repo.head_rev)
98
+ head_rev)
87
99
  end
88
100
  else
89
101
  # Since we also upload the index, we always need to run the
@@ -93,7 +105,7 @@ module TasteTester
93
105
  TasteTester::Hooks.pre_upload(TasteTester::Config.dryrun,
94
106
  @repo,
95
107
  @server.latest_uploaded_ref,
96
- @repo.head_rev)
108
+ head_rev)
97
109
  end
98
110
  begin
99
111
  time(logger) { partial }
@@ -105,16 +117,99 @@ module TasteTester
105
117
  TasteTester::Hooks.post_upload(TasteTester::Config.dryrun,
106
118
  @repo,
107
119
  @server.latest_uploaded_ref,
108
- @repo.head_rev)
120
+ head_rev)
109
121
  end
110
122
  end
111
123
 
112
- @server.latest_uploaded_ref = @repo.head_rev
124
+ @server.latest_uploaded_ref = head_rev
125
+ @server.last_upload_time = Time.new.strftime('%Y-%m-%d %H:%M:%S')
113
126
  end
114
127
 
115
128
  private
116
129
 
130
+ def populate(stream, writer, path, destination)
131
+ full_path = File.join(File.join(TasteTester::Config.repo, path))
132
+ return unless File.directory?(full_path)
133
+ # everything is relative to the repo dir. chdir makes handling all the
134
+ # paths within this simpler
135
+ Dir.chdir(full_path) do
136
+ Find.find('.') do |p|
137
+ # ignore current directory. The File.directory? would also skip it,
138
+ # but we need to do it early because the string is too short for the
139
+ # next statement.
140
+ next if p == '.'
141
+ # paths are enumerated as relative to the input path '.', so we get
142
+ # './dir/file'. Stripping off the first two characters gives us a
143
+ # a cleaner 'dir/file' path.
144
+ name = File.join(destination, p[2..-1])
145
+ if File.directory?(p)
146
+ # skip it. This also handles symlinks to directories which aren't
147
+ # useful either.
148
+ elsif File.symlink?(p)
149
+ # tar handling of filenames > 100 characters gets complex. We'd use
150
+ # split_name from Minitar, but it's a private method. It's
151
+ # reasonable to assume that all symlink names in the bundle are
152
+ # less than 100 characters long. Long term, the version of minitar
153
+ # in chefdk should be upgraded.
154
+ fail 'Add support for long symlink paths' if name.size > 100
155
+ # The version of Minitar included in chefdk does not support
156
+ # symlinks directly. Therefore we use direct writes to the
157
+ # underlying stream to reproduce the symlinks
158
+ symlink = {
159
+ :name => name,
160
+ :mode => 0644,
161
+ :typeflag => '2',
162
+ :size => 0,
163
+ :linkname => File.readlink(p),
164
+ :prefix => '',
165
+ }
166
+ stream.write(Minitar::PosixHeader.new(symlink))
167
+ else
168
+ File.open(p, 'rb') do |r|
169
+ writer.add_file_simple(
170
+ name, :mode => 0644, :size => File.size(r)
171
+ ) do |d, _opts|
172
+ IO.copy_stream(r, d)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ def bundle_upload
181
+ dest = File.join(@server.bundle_dir, 'tt.tgz')
182
+ begin
183
+ Tempfile.create(['tt', '.tgz'], @server.bundle_dir) do |tempfile|
184
+ stream = Zlib::GzipWriter.new(tempfile)
185
+ Minitar::Writer.open(stream) do |writer|
186
+ TasteTester::Config.relative_cookbook_dirs.each do |cb_dir|
187
+ populate(stream, writer, cb_dir, 'cookbooks')
188
+ end
189
+ populate(
190
+ stream, writer, TasteTester::Config.relative_role_dir, 'roles'
191
+ )
192
+ populate(
193
+ stream, writer, TasteTester::Config.relative_databag_dir,
194
+ 'databag'
195
+ )
196
+ end
197
+ stream.close
198
+ File.rename(tempfile.path, dest)
199
+ end
200
+ rescue Errno::ENOENT
201
+ # Normally the temporary file is renamed to the dest name. If this
202
+ # happens, then the cleanup of of the temporary file doesn't work,
203
+ # but this is fine and expected.
204
+ nil
205
+ end
206
+ end
207
+
117
208
  def full
209
+ if TasteTester::Config.bundle
210
+ bundle_upload
211
+ return
212
+ end
118
213
  logger.warn('Doing full upload')
119
214
  @knife.cookbook_upload_all
120
215
  @knife.role_upload_all
@@ -122,6 +217,11 @@ module TasteTester
122
217
  end
123
218
 
124
219
  def partial
220
+ if TasteTester::Config.bundle
221
+ logger.info('No partial support for bundle mode, doing full upload')
222
+ bundle_upload
223
+ return
224
+ end
125
225
  logger.info('Doing differential upload from ' +
126
226
  @server.latest_uploaded_ref)
127
227
  changeset = BetweenMeals::Changeset.new(