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'
            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)
                                    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
                        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
     |