Class: Onceover::CLI::Run::Diff

Inherits:
Object
  • Object
show all
Defined in:
lib/onceover/octocatalog/diff/cli.rb

Class Method Summary collapse

Class Method Details

.commandObject



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
145
146
147
148
149
150
151
152
153
# File 'lib/onceover/octocatalog/diff/cli.rb', line 5

def self.command
  @cmd ||= Cri::Command.define do
    name 'diff'
    usage 'diff'
    summary "Diff two versions of the controlrepo's compiled catalogs"
    description <<-DESCRIPTION
This uses octocatalog-diff to run diffs on all things in the test matrix
instead of actually testing them. Requires two branches, tags or
revisions to compare between.
    DESCRIPTION

    option :f,  :from, 'branch to compare from', argument: :required
    option :t,  :to,   'branch to compare to', argument: :required

    run do |opts, args, cmd|
      require 'facter'
      require 'colored'

      #TODO: Allow for custom arguments
      repo        = Onceover::Controlrepo.new(opts)
      test_config = Onceover::TestConfig.new(repo.onceover_yaml, opts)
      num_threads = (Facter.value('processors')['count'] / 2)
      tests = test_config.run_filters(Onceover::Test.deduplicate(test_config.spec_tests))

      @queue = tests.inject(Queue.new, :push)
      @results = []

      @threads = Array.new(num_threads) do
        Thread.new do
          r10k_cache_dir = Dir.mktmpdir('r10k_cache')
          r10k_config = {
            'cachedir' => r10k_cache_dir,
          }
          logger.debug "Creating r10k cache for thread at #{r10k_cache_dir}"
          File.write("#{r10k_cache_dir}/r10k.yaml",r10k_config.to_yaml)

          until @queue.empty?
            test = @queue.shift

            logger.info "Preparing environment for #{test.classes[0].name} on #{test.nodes[0].name}"
            logger.debug "Creating temp directory"
            tempdir = Dir.mktmpdir(test.to_s)
            logger.debug "Temp directory created at #{tempdir}"

            logger.debug "Copying controlrepo to #{tempdir}"
            FileUtils.copy_entry(repo.root,tempdir)

            # Copy all of the factsets over in reverse order so that
            # local ones override vendored ones
            logger.debug "Deploying vendored factsets"
            deduped_factsets = repo.facts_files.reverse.inject({}) do |hash, file|
              hash[File.basename(file)] = file
              hash
            end

            deduped_factsets.each do |basename,path|
              facts = JSON.load(File.read(path))
              File.open("#{tempdir}/spec/factsets/#{File.basename(path,'.*')}.yaml", 'w') { |f| f.write facts.to_yaml }
            end

            if File.directory?("#{r10k_cache_dir}/modules")
              logger.debug "Copying modules from thread cache to #{tempdir}"
              FileUtils.copy_entry("#{r10k_cache_dir}/modules","#{tempdir}/modules")
            end

            logger.info "Deploying Puppetfile for #{test.classes[0].name} on #{test.nodes[0].name}"
            r10k_cmd = "r10k puppetfile install --verbose --color --puppetfile #{repo.puppetfile} --config #{r10k_cache_dir}/r10k.yaml"
            Open3.popen3(r10k_cmd, :chdir => tempdir) do |stdin, stdout, stderr, wait_thr|
              exit_status = wait_thr.value
              if exit_status.exitstatus != 0
                STDOUT.puts stdout.read
                STDERR.puts stderr.read
                abort "R10k encountered an error, see the logs for details"
              end
            end

            # TODO: Improve the way this works so that it doesn't blat site.pp
            logger.debug "Creating before script that overwrites site.pp"
            class_name = test.classes[0].name
            template_dir = File.expand_path('../../../../templates',File.dirname(__FILE__))
            template = File.read(File.expand_path("./change_manifest.rb.erb",template_dir))
            File.write("#{tempdir}/bootstrap_script.rb",ERB.new(template, nil, '-').result(binding))
            FileUtils.chmod("u=rwx","#{tempdir}/bootstrap_script.rb")

            logger.debug "Getting Puppet binary"
            binary = `which puppet`.chomp

            logger.debug "Running Octocatalog diff"
            logger.info "Compiling catalogs for #{test.classes[0].name} on #{test.nodes[0].name}"

            command_prefix = ENV['BUNDLE_GEMFILE'] ? 'bundle exec ' : ''

            command_args = [
              '--fact-file',
              "#{tempdir}/spec/factsets/#{test.nodes[0].name}.yaml",
              '--from',
              opts[:from],
              '--to',
              opts[:to],
              '--basedir',
              tempdir,
              '--puppet-binary',
              binary,
              '--bootstrap-script',
              "'#{tempdir}/bootstrap_script.rb'",
              '--hiera-config',
              repo.hiera_config_file,
              '--pass-env-vars',
              ENV.keys.keep_if {|k| k =~ /^RUBY|^BUNDLE/ }.join(',')
            ]

            cmd = "#{command_prefix}octocatalog-diff #{command_args.join(' ')}"
            logger.debug "Running: #{cmd}"
            Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
              exit_status = wait_thr.value
              @results << {
                stdout: stdout.read,
                stderr: stderr.read,
                exit_status: exit_status.exitstatus,
                test: test
              }
            end
            logger.info "Storing results for #{test.classes[0].name} on #{test.nodes[0].name}"

            logger.debug "Backing up modules to thread cache #{tempdir}"
            FileUtils.mv("#{tempdir}/modules","#{r10k_cache_dir}/modules",:force => true)

            logger.debug "Removing temporary build cache"
            FileUtils.rm_r(tempdir)
          end

          FileUtils.rm_r(r10k_cache_dir)
        end
      end

      @threads.each(&:join)
      @results.each do |result|
        puts "#{"Test:".bold} #{result[:test].classes[0].name} on #{result[:test].nodes[0].name}"
        puts "#{"Exit:".bold} #{result[:exit_status]}"
        puts "#{"Status:".bold} #{"changes".yellow}" if result[:exit_status] == 2
        puts "#{"Status:".bold} #{"no differences".green}" if result[:exit_status] == 0
        puts "#{"Status:".bold} #{"failed".red}" if result[:exit_status] == 1
        puts "#{"Results:".bold}\n#{result[:stdout]}\n" if result[:exit_status] == 2
        puts "#{"Errors:".bold}\n#{result[:stderr]}\n" if result[:exit_status] == 1
        puts ""
      end
    end
  end
end