jamie 0.1.0.alpha16 → 0.1.0.alpha17

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/Rakefile CHANGED
@@ -19,7 +19,7 @@ end
19
19
 
20
20
  Tailor::RakeTask.new
21
21
 
22
- desc "yep"
22
+ desc "Display LOC stats"
23
23
  task :stats do
24
24
  sh "countloc -r lib/jamie lib/jamie.rb"
25
25
  end
data/lib/jamie/cli.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'erb'
4
3
  require 'ostruct'
5
4
  require 'thor'
6
5
 
@@ -25,7 +24,7 @@ module Jamie
25
24
  say Array(result).map{ |i| i.name }.join("\n")
26
25
  end
27
26
 
28
- [:create, :converge, :setup, :verify, :test, :destroy].each do |action|
27
+ [:create, :converge, :setup, :verify, :destroy].each do |action|
29
28
  desc(
30
29
  "#{action} (all ['REGEX']|[INSTANCE])",
31
30
  "#{action.capitalize} one or more instances"
@@ -33,6 +32,28 @@ module Jamie
33
32
  define_method(action) { |*args| exec_action(action) }
34
33
  end
35
34
 
35
+ desc "test (all ['REGEX']|[INSTANCE]) [opts]", "Test one or more instances"
36
+ long_desc <<-DESC
37
+ Test one or more instances
38
+
39
+ There are 3 post-verify modes for instance cleanup, triggered with
40
+ the `--destroy' flag:
41
+
42
+ * passing: instances passing verify will be destroyed afterwards.\n
43
+ * always: instances will always be destroyed afterwards.\n
44
+ * never: instances will never be destroyed afterwards.
45
+ DESC
46
+ method_option :destroy, :aliases => "-d", :default => "passing",
47
+ :desc => "Destroy strategy to use after testing (passing, always, never)."
48
+ def test(*args)
49
+ destroy_mode = options[:destroy]
50
+ if ! %w{passing always never}.include?(options[:destroy])
51
+ raise ArgumentError, "Destroy mode must be passing, always, or never."
52
+ end
53
+ result = parse_subcommand(args[0], args[1])
54
+ Array(result).each { |instance| instance.test(destroy_mode.to_sym) }
55
+ end
56
+
36
57
  desc "version", "Print Jamie's version information"
37
58
  def version
38
59
  say "Jamie version #{Jamie::VERSION}"
@@ -0,0 +1,58 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2012, Fletcher Nichol
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
+ require 'jamie'
20
+
21
+ module Jamie
22
+
23
+ module Driver
24
+
25
+ # Dummy driver for Jamie.
26
+ class Dummy < Jamie::Driver::Base
27
+
28
+ def create(instance, state)
29
+ state['my_id'] = "#{instance.name}-#{Time.now.to_i}"
30
+ report(:create, instance, state)
31
+ end
32
+
33
+ def converge(instance, state)
34
+ report(:converge, instance, state)
35
+ end
36
+
37
+ def setup(instance, state)
38
+ report(:setup, instance, state)
39
+ end
40
+
41
+ def verify(instance, state)
42
+ report(:verify, instance, state)
43
+ end
44
+
45
+ def destroy(instance, state)
46
+ report(:destroy, instance, state)
47
+ state.delete('my_id')
48
+ end
49
+
50
+ private
51
+
52
+ def report(action, instance, state)
53
+ puts "[Dummy] Action ##{action} called on " +
54
+ "instance=#{instance} with state=#{state}"
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/jamie/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Jamie
4
4
 
5
- VERSION = "0.1.0.alpha16"
5
+ VERSION = "0.1.0.alpha17"
6
6
  end
data/lib/jamie.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'base64'
4
4
  require 'delegate'
5
5
  require 'digest'
6
+ require 'erb'
6
7
  require 'fileutils'
7
8
  require 'json'
8
9
  require 'mixlib/shellout'
@@ -150,7 +151,11 @@ module Jamie
150
151
  end
151
152
 
152
153
  def yaml
153
- @yaml ||= YAML.load_file(File.expand_path(yaml_file)).rmerge(local_yaml)
154
+ @yaml ||= YAML.load(yaml_contents).rmerge(local_yaml)
155
+ end
156
+
157
+ def yaml_contents
158
+ ERB.new(IO.read(File.expand_path(yaml_file))).result
154
159
  end
155
160
 
156
161
  def local_yaml_file
@@ -161,7 +166,7 @@ module Jamie
161
166
  def local_yaml
162
167
  @local_yaml ||= begin
163
168
  if File.exists?(local_yaml_file)
164
- YAML.load_file(local_yaml_file)
169
+ YAML.load(ERB.new(IO.read(local_yaml_file)).result)
165
170
  else
166
171
  Hash.new
167
172
  end
@@ -364,10 +369,7 @@ module Jamie
364
369
  # @todo rescue Driver::ActionFailed and return some kind of null object
365
370
  # to gracfully stop action chaining
366
371
  def create
367
- puts "-----> Creating instance #{name}"
368
- action(:create) { |state| platform.driver.create(self, state) }
369
- puts " Creation of instance #{name} complete."
370
- self
372
+ transition_to(:create)
371
373
  end
372
374
 
373
375
  # Converges this running instance.
@@ -378,10 +380,7 @@ module Jamie
378
380
  # @todo rescue Driver::ActionFailed and return some kind of null object
379
381
  # to gracfully stop action chaining
380
382
  def converge
381
- puts "-----> Converging instance #{name}"
382
- action(:converge) { |state| platform.driver.converge(self, state) }
383
- puts " Convergence of instance #{name} complete."
384
- self
383
+ transition_to(:converge)
385
384
  end
386
385
 
387
386
  # Sets up this converged instance for suite tests.
@@ -392,10 +391,7 @@ module Jamie
392
391
  # @todo rescue Driver::ActionFailed and return some kind of null object
393
392
  # to gracfully stop action chaining
394
393
  def setup
395
- puts "-----> Setting up instance #{name}"
396
- action(:setup) { |state| platform.driver.setup(self, state) }
397
- puts " Setup of instance #{name} complete."
398
- self
394
+ transition_to(:setup)
399
395
  end
400
396
 
401
397
  # Verifies this set up instance by executing suite tests.
@@ -406,10 +402,7 @@ module Jamie
406
402
  # @todo rescue Driver::ActionFailed and return some kind of null object
407
403
  # to gracfully stop action chaining
408
404
  def verify
409
- puts "-----> Verifying instance #{name}"
410
- action(:verify) { |state| platform.driver.verify(self, state) }
411
- puts " Verification of instance #{name} complete."
412
- self
405
+ transition_to(:verify)
413
406
  end
414
407
 
415
408
  # Destroys this instance.
@@ -420,40 +413,75 @@ module Jamie
420
413
  # @todo rescue Driver::ActionFailed and return some kind of null object
421
414
  # to gracfully stop action chaining
422
415
  def destroy
423
- puts "-----> Destroying instance #{name}"
424
- action(:destroy) { |state| platform.driver.destroy(self, state) }
425
- destroy_state
426
- puts " Destruction of instance #{name} complete."
427
- self
416
+ transition_to(:destroy)
428
417
  end
429
418
 
430
419
  # Tests this instance by creating, converging and verifying. If this
431
420
  # instance is running, it will be pre-emptively destroyed to ensure a
432
421
  # clean slate. The instance will be left post-verify in a running state.
433
422
  #
434
- # @see #destroy
435
- # @see #create
436
- # @see #converge
437
- # @see #setup
438
- # @see #verify
423
+ # @param destroy_mode [Symbol] strategy used to cleanup after instance
424
+ # has finished verifying (default: `:passing`)
439
425
  # @return [self] this instance, used to chain actions
440
426
  #
441
427
  # @todo rescue Driver::ActionFailed and return some kind of null object
442
428
  # to gracfully stop action chaining
443
- def test
429
+ def test(destroy_mode = :passing)
444
430
  puts "-----> Cleaning up any prior instances of #{name}"
445
431
  destroy
446
432
  puts "-----> Testing instance #{name}"
447
- create
448
- converge
449
- setup
450
433
  verify
434
+ destroy if destroy_mode == :passing
451
435
  puts " Testing of instance #{name} complete."
452
436
  self
437
+ ensure
438
+ destroy if destroy_mode == :always
453
439
  end
454
440
 
455
441
  private
456
442
 
443
+ def transition_to(desired)
444
+ FSM.actions(last_action, desired).each do |transition|
445
+ send("#{transition}_action")
446
+ end
447
+ end
448
+
449
+ def create_action
450
+ puts "-----> Creating instance #{name}"
451
+ action(:create) { |state| platform.driver.create(self, state) }
452
+ puts " Creation of instance #{name} complete."
453
+ self
454
+ end
455
+
456
+ def converge_action
457
+ puts "-----> Converging instance #{name}"
458
+ action(:converge) { |state| platform.driver.converge(self, state) }
459
+ puts " Convergence of instance #{name} complete."
460
+ self
461
+ end
462
+
463
+ def setup_action
464
+ puts "-----> Setting up instance #{name}"
465
+ action(:setup) { |state| platform.driver.setup(self, state) }
466
+ puts " Setup of instance #{name} complete."
467
+ self
468
+ end
469
+
470
+ def verify_action
471
+ puts "-----> Verifying instance #{name}"
472
+ action(:verify) { |state| platform.driver.verify(self, state) }
473
+ puts " Verification of instance #{name} complete."
474
+ self
475
+ end
476
+
477
+ def destroy_action
478
+ puts "-----> Destroying instance #{name}"
479
+ action(:destroy) { |state| platform.driver.destroy(self, state) }
480
+ destroy_state
481
+ puts " Destruction of instance #{name} complete."
482
+ self
483
+ end
484
+
457
485
  def action(what)
458
486
  state = load_state
459
487
  yield state if block_given?
@@ -482,6 +510,46 @@ module Jamie
482
510
  platform.driver['jamie_root'], ".jamie", "#{name}.yml"
483
511
  ))
484
512
  end
513
+
514
+ def last_action
515
+ load_state['last_action']
516
+ end
517
+
518
+ # The simplest finite state machine pseudo-implementation needed to manage
519
+ # an Instance.
520
+ class FSM
521
+
522
+ # Returns an Array of all transitions to bring an Instance from its last
523
+ # reported transistioned state into the desired transitioned state.
524
+ #
525
+ # @param last [String,Symbol,nil] the last known transitioned state of
526
+ # the Instance, defaulting to `nil` (for unknown or no history)
527
+ # @param desired [String,Symbol] the desired transitioned state for the
528
+ # Instance
529
+ # @return [Array<Symbol>] an Array of transition actions to perform
530
+ def self.actions(last = nil, desired)
531
+ last_index = index(last)
532
+ desired_index = index(desired)
533
+
534
+ if last_index == desired_index || last_index > desired_index
535
+ Array(TRANSITIONS[desired_index])
536
+ else
537
+ TRANSITIONS.slice(last_index + 1, desired_index - last_index)
538
+ end
539
+ end
540
+
541
+ private
542
+
543
+ TRANSITIONS = [ :destroy, :create, :converge, :setup, :verify ]
544
+
545
+ def self.index(transition)
546
+ if transition.nil?
547
+ 0
548
+ else
549
+ TRANSITIONS.find_index { |t| t == transition.to_sym }
550
+ end
551
+ end
552
+ end
485
553
  end
486
554
 
487
555
  # Command string generator to interface with Jamie Runner (jr). The
@@ -781,7 +849,7 @@ module Jamie
781
849
  end
782
850
 
783
851
  def converge(instance, state)
784
- ssh_args = generate_ssh_args(state)
852
+ ssh_args = build_ssh_args(state)
785
853
 
786
854
  install_omnibus(ssh_args) if config['require_chef_omnibus']
787
855
  prepare_chef_home(ssh_args)
@@ -790,7 +858,7 @@ module Jamie
790
858
  end
791
859
 
792
860
  def setup(instance, state)
793
- ssh_args = generate_ssh_args(state)
861
+ ssh_args = build_ssh_args(state)
794
862
 
795
863
  if instance.jr.setup_cmd
796
864
  ssh(ssh_args, instance.jr.setup_cmd)
@@ -798,7 +866,7 @@ module Jamie
798
866
  end
799
867
 
800
868
  def verify(instance, state)
801
- ssh_args = generate_ssh_args(state)
869
+ ssh_args = build_ssh_args(state)
802
870
 
803
871
  if instance.jr.run_cmd
804
872
  ssh(ssh_args, instance.jr.sync_cmd)
@@ -812,11 +880,12 @@ module Jamie
812
880
 
813
881
  protected
814
882
 
815
- def generate_ssh_args(state)
816
- [ state['hostname'],
817
- config['username'],
818
- { :password => config['password'] }
819
- ]
883
+ def build_ssh_args(state)
884
+ opts = Hash.new
885
+ opts[:password] = config['password'] if config['password']
886
+ opts[:keys] = Array(config['ssh_key']) if config['ssh_key']
887
+
888
+ [ state['hostname'], config['username'], opts ]
820
889
  end
821
890
 
822
891
  def chef_home
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jamie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha16
4
+ version: 0.1.0.alpha17
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -174,6 +174,7 @@ files:
174
174
  - jamie.gemspec
175
175
  - lib/jamie.rb
176
176
  - lib/jamie/cli.rb
177
+ - lib/jamie/driver/dummy.rb
177
178
  - lib/jamie/rake_tasks.rb
178
179
  - lib/jamie/thor_tasks.rb
179
180
  - lib/jamie/version.rb
@@ -199,7 +200,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
199
200
  version: '0'
200
201
  segments:
201
202
  - 0
202
- hash: -1684640608008309798
203
+ hash: -4096747563693358273
203
204
  required_rubygems_version: !ruby/object:Gem::Requirement
204
205
  none: false
205
206
  requirements: