jamie 0.1.0.alpha16 → 0.1.0.alpha17

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