Module: VirtualMonkey::Command

Defined in:
lib/virtualmonkey/command.rb,
lib/virtualmonkey/command/run.rb,
lib/virtualmonkey/command/list.rb,
lib/virtualmonkey/command/clone.rb,
lib/virtualmonkey/command/troop.rb,
lib/virtualmonkey/command/create.rb,
lib/virtualmonkey/command/destroy.rb

Class Method Summary collapse

Class Method Details

.cloneObject

monkey clone –deployment name –feature testcase.rb –breakpoint 4 –copies 7



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/virtualmonkey/command/clone.rb', line 5

def self.clone
  options = Trollop::options do
    opt :deployment, "regex string to use for matching deployment", :type => :string, :short => '-d', :required => true
    opt :feature, "path to feature(s) to run against the deployments", :type => :string
    opt :breakpoint, "feature file line to stop at", :type => :integer, :short => '-b'
    opt :copies, "number of copies to make (default is 1)", :type => :integer, :short => '-c'
  end

  options[:copies] = 1 unless options[:copies] > 1
  options[:no_resume] = true
  dm = DeploymentMonk.new(options[:deployment])
  if dm.deployments.length > 1
    raise "FATAL: Ambiguous Regex; more than one deployment matched /#{options[:deployment]}/"
  elsif dm.deployments.length < 1
    raise "FATAL: Ambiguous Regex; no deployment matched /#{options[:deployment]}/"
  end
  origin = dm.deployments.first
  do_these ||= []
  # clone deployment
  for i in 1 .. options[:copies]
    new_deploy = origin.clone
    new_deploy.nickname = "#{origin.nickname}-clone-#{i}"
    new_deploy.save
    do_these << new_deploy
  end

  # run to breakpoint
  if options[:feature]
    EM.run {
      cm = CukeMonk.new
      cm.options = options
      do_these.each do |deploy|
        cm.run_test(deploy, options[:feature])
      end

      watch = EM.add_periodic_timer(10) {
        if cm.all_done?
          watch.cancel
        end
        cm.watch_and_report
      }
    }
  end
end

.createObject

monkey create –server_template_ids 123,123 –common_inputs blah.json –feature simple.feature –tag unique_name –TBD:filter?



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/virtualmonkey/command/create.rb', line 5

def self.create
  options = Trollop::options do
    opt :server_template_ids, "ServerTemplate ids to use for creating the deployment.  Use one ID per server that you would like to be in the deployment.  Accepts space separated integers, or one argument per id. Eg. -s 23747 23747", :type => :integers, :required => true, :short => '-s'
    opt :common_inputs, "Paths to common input json files to load and set on all deployments.  Accepts space separated pathnames or one argument per pathname.  Eg. -c config/mysql_inputs.json -c config/other_inputs.json", :type => :strings, :required => true, :short => '-c'
    opt :tag, "Tag to use as nickname prefix for all deployments.", :type => :string, :required => true, :short => '-t'
    opt :cloud_variables, "Path to json file containing common inputs and variables per cloud. See config/cloud_variables.json.example", :type => :string, :required => true, :short => '-v'
    opt :no_spot, "Do not use spot instances"
  end
  @dm = DeploymentMonk.new(options[:tag], options[:server_template_ids])
  @dm.variables_for_cloud = JSON::parse(IO.read(options[:cloud_variables]))
  options[:common_inputs].each do |cipath|
    @dm.load_common_inputs(cipath)
  end  
  @dm.generate_variations(options)
end

.destroyObject

monkey destroy –tag unique_tag



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/virtualmonkey/command/destroy.rb', line 5

def self.destroy
  options = Trollop::options do
    opt :tag, "Tag to match prefix of the deployments to destroy.", :type => :string, :required => true, :short => '-t'
    opt :terminate, "Terminate using the specified runner", :type => :string, :required => true, :short => "-r"
    opt :no_delete, "only terminate, no deletion."
    opt :yes, "Turn off confirmation for destroy operation"
  end
  begin
    eval("VirtualMonkey::#{options[:terminate]}.new('fgasvgreng243o520sdvnsals')") if options[:terminate]
  rescue Exception => e
    raise e unless e.message =~ /Could not find a deployment named/
    options[:terminate] = "SimpleRunner" if options[:terminate]
  end
  @dm = DeploymentMonk.new(options[:tag])
#      nicks = @dm.deployments.map &:nickname
  nicks = @dm.deployments.map { |d| d.nickname }
  nicks.each { |n| say n }
  unless options[:yes]
    confirm = ask("Really destroy these #{nicks.length} deployments (y/n)?", lambda { |ans| true if (ans =~ /^[y,Y]{1}/) })
    raise "Aborting." unless confirm
  end

  global_state_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "test_states")
  @dm.deployments.each do |deploy|
    @runner = eval("VirtualMonkey::#{options[:terminate]}.new(deploy.nickname)")
    @runner.behavior(:stop_all, false)
    state_dir = File.join(global_state_dir, deploy.nickname)
    if File.directory?(state_dir)
      puts "Deleting state files for #{deploy.nickname}..."
      Dir.new(state_dir).each do |state_file|
        if File.extname(state_file) =~ /((rb)|(feature))/
          File.delete(File.join(state_dir, state_file))
        end
      end
      Dir.rmdir(state_dir)
    end
    if @runner.respond_to?(:release_dns) and not options[:no_delete]
      @runner.behavior(:release_dns)
    end
  end

  @dm.destroy_all unless options[:no_delete]
  say "monkey done."
end

.goObject

Parses the initial command string, removing it from ARGV, then runs command.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/virtualmonkey/command.rb', line 15

def self.go
  command = ARGV.shift
  case command
    when "create"
      VirtualMonkey::Command.create
    when "destroy"
      VirtualMonkey::Command.destroy
    when "run"
      VirtualMonkey::Command.run
    when "list"
      VirtualMonkey::Command.list
    when "troop"
      VirtualMonkey::Command.troop
    when "clone"
      VirtualMonkey::Command.clone
    when "help" || "--help" || "-h"
      puts "Help usage: monkey <command> --help"
      puts "Valid commands for monkey: create, destroy, list, run, troop, clone or help"
    else
      STDERR.puts "Invalid command #{command}: You need to specify a command for monkey: create, destroy, list, run, troop, clone or help\n"
      exit(1)
  end
end

.listObject



3
4
5
6
7
8
# File 'lib/virtualmonkey/command/list.rb', line 3

def self.list
  options = Trollop::options do
    opt :tags, "List deployment set tags", :type => :string, :required => true
  end
  DeploymentMonk.new(options[:tags]).deployments.each { |d| puts d.nickname }
end

.runObject

monkey run –feature –tag –only <regex to match on deploy nickname>



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/virtualmonkey/command/run.rb', line 8

def self.run
  options = Trollop::options do
    opt :feature, "path to feature(s) to run against the deployments", :type => :string, :required => true
    opt :breakpoint, "feature file line to stop at", :type => :integer, :short => '-b'
    opt :tag, "Tag to match prefix of the deployments.", :type => :string, :required => true, :short => "-t"
    opt :only, "regex string to use for subselection matching on deployments.  Eg. --only x86_64", :type => :string
    opt :terminate, "Terminate using the specified runner if feature successfully completes. (No destroy)", :type => :string, :short => "-r"
    opt :no_resume, "Do not use current test-in-progress, start from scratch", :short => "-n"
    opt :yes, "Turn off confirmation", :short => "-y"
  end

  global_state_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "test_states")
  begin
    eval("VirtualMonkey::#{options[:terminate]}.new('fgasvgreng243o520sdvnsals')") if options[:terminate]
  rescue Exception => e
    raise e unless e.message =~ /Could not find a deployment named/
    options[:terminate] = "SimpleRunner" if options[:terminate]
  end
  EM.run {
    cm = CukeMonk.new
    dm = DeploymentMonk.new(options[:tag])
    if options[:only]
      do_these = dm.deployments.select { |d| d.nickname =~ /#{options[:only]}/ }
    else
      do_these = dm.deployments
    end

    unless options[:no_resume]
      temp = do_these.select do |d|
        File.exist?(File.join(global_state_dir, d.nickname, File.basename(options[:feature])))
      end
      do_these = temp if temp.length > 0
    end

    cm.options = options
    raise "No deployments matched!" unless do_these.length > 0
    do_these.each { |d| say d.nickname }

    unless options[:yes]
      confirm = ask("Run tests on these #{do_these.length} deployments (y/n)?", lambda { |ans| true if (ans =~ /^[y,Y]{1}/) })
      raise "Aborting." unless confirm
    end

    remaining_jobs = cm.jobs.dup
    do_these.each do |deploy|
      cm.run_test(deploy, options[:feature])
    end

    watch = EM.add_periodic_timer(10) {
      cm.watch_and_report
      if cm.all_done?
        watch.cancel
      end
      if options[:terminate]
        remaining_jobs.each do |job|
          if job.status == 0
            @runner = eval("VirtualMonkey::#{options[:terminate]}.new(job.deployment.nickname)")
            puts "destroying successful deployment: #{@runner.deployment.nickname}"
            @runner.behavior(:stop_all, false)
            remaining_jobs.delete(job)
          end
        end
      end
    }

  }
end

.troopObject

This command does all the steps create/run/conditionaly destroy



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/virtualmonkey/command/troop.rb', line 4

def self.troop
  options = Trollop::options do
    text "This command performs all the operations of the monkey in one execution.  Create/Run/Destroy"
    opt :file, "troop config, see config/troop/*sample.json for example format", :type => :string, :required => true
    opt :no_spot, "do not use spot instances"
    opt :step, "use the troop config file to do either: create, run, or destroy", :type => :string
    opt :tag, "add an additional tag to the deployments", :type => :string
    opt :create, "interactive mode: create troop config"
    opt :mci_override, "list of mcis to use instead of the ones from the server template. expects full hrefs.", :type => :string, :multi => true, :required => false
    opt :no_delete, "only terminate, no deletion.", :short => "-d"
  end

  # PATHs SETUP
  features_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "features")
  features_glob = Dir.glob(File.join(features_dir, "**"))
  features_glob = features_glob.collect { |c| File.basename(c) }

  config_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "config")
  cloud_variables_glob = Dir.glob(File.join(config_dir, "cloud_variables", "**"))
  cloud_variables_glob = cloud_variables_glob.collect { |c| File.basename(c) }
  common_inputs_glob = Dir.glob(File.join(config_dir, "common_inputs", "**"))
  common_inputs_glob = common_inputs_glob.collect { |c| File.basename(c) }
  global_state_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "test_states")
  options[:tag] += "-" if options[:tag]
  options[:tag] = "" unless options[:tag]
  
  # CREATE NEW CONFIG
  if options[:create]
    troop_config = {}
    troop_config[:tag] = ask("What tag to use for creating the deployments?")
    troop_config[:server_template_ids] = ask("What Server Template ids would you like to use to create the deployments (comma delimited)?").split(",")
    troop_config[:server_template_ids].each {|st| st.strip!}

    troop_config[:runner] = 
      choose do |menu|
        menu.prompt = "What kind of deployment is this (runner type)?"
        menu.choice("MysqlToolboxRunner")
        menu.choice("MysqlRunner")
        menu.choice("SimpleRunner")
      end

    troop_config[:cloud_variables] =
      choose do |menu|
        menu.prompt = "Which cloud_variables config file?"
        menu.index = :number
        menu.choices(*cloud_variables_glob)
      end

    troop_config[:common_inputs] =
      choose do |menu|
        menu.prompt = "Which common_inputs config file?"
        menu.index = :number
        menu.choices(*common_inputs_glob)
      end

    troop_config[:feature] = 
      choose do |menu|
        menu.prompt = "Which feature file?"
        menu.index = :number
        menu.choices(*features_glob)
      end
    
    write_out = troop_config.to_json( :indent => "  ", 
                                      :object_nl => "\n",
                                      :array_nl => "\n" )
    File.open(options[:file], "w") { |f| f.write(write_out) }
    say("created config file #{options[:file]}")
    say("Done.")
  else
    # Execute Main
    config = JSON::parse(IO.read(options[:file]))
    options[:step] = "all" unless options[:step]
    tag = options[:tag] + config['tag']

    # CREATE PHASE
    if options[:step] =~ /((all)|(create))/
      @dm = DeploymentMonk.new(tag, config['server_template_ids'])
      @dm.variables_for_cloud = JSON::parse(IO.read(File.join(config_dir, "cloud_variables", config['cloud_variables'])))
      config['common_inputs'].each do |cipath|
        @dm.load_common_inputs(File.join(config_dir, "common_inputs", cipath))
      end  
      @dm.generate_variations(options)
    end

    # RUN PHASE
    if options[:step] =~ /((all)|(run))/
      @dm = DeploymentMonk.new(tag) if options[:step] =~ /run/
      EM.run {
        @cm = CukeMonk.new
        @cm.options = {}
        remaining_jobs = @cm.jobs.dup
        @dm.deployments.each do |deploy|
          @cm.run_test(deploy, File.join(features_dir, config['feature']))
        end

        watch = EM.add_periodic_timer(10) {
          @cm.watch_and_report
          if @cm.all_done?
            watch.cancel 
          end
          remaining_jobs.each do |job|
            # destroy on success only (keep failed deploys)
            if job.status == 0 and options[:step] =~ /all/
              runner = eval("VirtualMonkey::#{config['runner']}.new(job.deployment.nickname)")
              puts "destroying successful deployment: #{runner.deployment.nickname}"
              runner.behavior(:stop_all, false)
              runner.deployment.destroy unless options[:no_delete]
              remaining_jobs.delete(job)
              if runner.respond_to?(:release_dns) and not options[:no_delete]
                runner.behavior(:release_dns)
              end
            end
          end
        }
      }
    end

    if options[:step] =~ /destroy/
      @dm = DeploymentMonk.new(tag)
      @dm.deployments.each do |deploy|
        runner = eval("VirtualMonkey::#{config['runner']}.new(deploy.nickname)")
        runner.behavior(:stop_all, false)
        state_dir = File.join(global_state_dir, deploy.nickname)
        if File.directory?(state_dir)
          puts "Deleting state files for #{deploy.nickname}..."
          Dir.new(state_dir).each do |state_file|
            if File.extname(state_file) =~ /((rb)|(feature))/
               File.delete(File.join(state_dir, state_file))
            end 
          end 
          Dir.rmdir(state_dir)
        end
        if runner.respond_to?(:release_dns) and not options[:no_delete]
          runner.behavior(:release_dns)
        end
      end
      @dm.destroy_all unless options[:no_delete]
    end
  end
  puts "Troop done."
end