puppetdb_spec.rb 11.2 KB
Newer Older
1 2 3 4 5 6
#!/usr/bin/env rspec

require 'spec_helper'
require 'puppet/reports'
require 'net/http'
require 'puppet/network/http_pool'
7
require 'puppet/util/puppetdb/command_names'
8
require 'puppet/util/puppetdb/config'
9
require 'json'
10 11 12 13 14

processor = Puppet::Reports.report(:puppetdb)

describe processor do

15 16
  subject {
    s = Puppet::Transaction::Report.new("foo").extend(processor)
17
    s.configuration_version = 123456789
18
    s.environment = "foo"
19 20
    s
  }
21 22 23 24 25 26

  context "#process" do

    let(:http) { mock "http" }
    let(:httpok) { Net::HTTPOK.new('1.1', 200, '') }

27 28
    def without_producer_timestamp(json_body)
      parsed = JSON.parse(json_body)
29
      parsed.delete("producer_timestamp")
30 31 32
      parsed.to_json
    end

33
    it "should POST the report command as a URL-encoded JSON string" do
34 35 36
      httpok.stubs(:body).returns '{"uuid": "a UUID"}'
      subject.stubs(:run_duration).returns(10)

37
      expected_body = subject.send(:report_to_hash).to_json
38 39 40

      Puppet::Network::HttpPool.expects(:http_instance).returns(http)
      http.expects(:post).with {|path, body, headers|
41
        expect(path).to include(Puppet::Util::Puppetdb::Command::CommandsUrl)
42 43 44 45

        # producer_timestamp is generated at submission time, so remove it from
        # the comparison
        expect(without_producer_timestamp(body)).to eq(without_producer_timestamp(expected_body))
46 47 48 49 50 51 52 53
      }.returns(httpok)

      subject.process
    end
  end

  context "#report_to_hash" do
    let (:resource) {
54 55 56 57
      stub("resource",
        { :pathbuilder => ["foo", "bar", "baz"],
          :path => "foo",
          :file => "foo",
58
          :line => 1,
59 60 61
          :tags => [],
          :title => "foo",
          :type => "foo" })
62 63 64 65 66 67 68 69 70 71
    }

    let (:status) {
        Puppet::Resource::Status.new(resource)
    }

    before :each do
      subject.add_resource_status(status)
    end

72
    it "should include the transaction uuid or nil" do
73
      subject.transaction_uuid = 'abc123'
74 75 76
      if defined?(subject.catalog_uuid) then
        subject.catalog_uuid = 'bde432'
      end
77

78
      result = subject.send(:report_to_hash)
79
      result["transaction_uuid"].should == 'abc123'
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

      # This won't be defined on < Puppet 4.3.3
      if defined?(subject.catalog_uuid) then
        result["catalog_uuid"].should == 'bde432'
      else
        result["catalog_uuid"].should == 'abc123'
      end
    end

    it "should include the code_id or nil" do
      if defined?(subject.code_id) then
        subject.code_id = 'bde432'
      end
      result = subject.send(:report_to_hash)
      if defined?(subject.code_id) then
        result["code_id"].should == 'bde432'
      else
        result["code_id"].should == nil
      end
    end

101 102 103 104 105 106
    it "should include the producer or nil" do
      Puppet[:node_name_value] = "foo"
      result = subject.send(:report_to_hash)
      result["producer"].should == "foo"
    end

107 108
    it "should include noop_pending or nil" do
      if defined?(subject.noop_pending) then
109
        subject.noop_pending = false
110 111 112 113 114 115 116 117 118
      end
      result = subject.send(:report_to_hash)
      if defined?(subject.noop_pending) then
        result["noop_pending"].should == false
      else
        result["noop_pending"].should == nil
      end
    end

119 120
    it "should include corrective_change or nil" do
      if defined?(subject.corrective_change) then
121
        subject.stubs(:corrective_change).returns(false)
122 123 124 125 126 127 128 129 130
      end
      result = subject.send(:report_to_hash)
      if defined?(subject.corrective_change) then
        result["corrective_change"].should == false
      else
        result["corrective_change"].should == nil
      end
    end

131 132 133 134 135 136 137 138 139 140
    it "should include the cached_catalog_status or nil" do
      if defined?(subject.cached_catalog_status) then
        subject.cached_catalog_status = 'not_used'
      end
      result = subject.send(:report_to_hash)
      if defined?(subject.cached_catalog_status) then
        result["cached_catalog_status"].should == 'not_used'
      else
        result["cached_catalog_status"].should == nil
      end
141 142
    end

143 144 145 146 147 148
    context "noop run" do
      before :all do
        Puppet[:noop] = true
      end

      it "should include truthy noop flag" do
149
        unless (defined?(subject.noop) && (not subject.noop.nil?)) then
150 151 152 153 154 155
          event = Puppet::Transaction::Event.new
          event.status = "noop"
          status.add_event(event)
        end
        result = subject.send(:report_to_hash)
        result["noop"].should == true
156 157 158
      end
    end

159 160 161 162 163 164
    context "enforcement run" do
      before :all do
        Puppet[:noop] = false
      end

      it "should include falsey noop flag" do
165
        unless (defined?(subject.noop) && (not subject.noop.nil?)) then
166 167 168 169 170 171
          event = Puppet::Transaction::Event.new
          event.status = "success"
          status.add_event(event)
        end
        result = subject.send(:report_to_hash)
        result["noop"].should == false
172 173 174
      end
    end

175 176 177 178 179 180 181 182 183
    context "start/end time" do
      before :each do
        subject.add_metric("time", {"total" => 10})
      end

      it "should base run duration off of the 'time'->'total' metric" do
        subject.send(:run_duration).should == 10
      end

184
      it "should use run_duration to calculate the end_time" do
185
        result = subject.send(:report_to_hash)
186
        duration = Time.parse(result["end_time"]) - Time.parse(result["start_time"])
187 188 189 190 191 192 193 194 195 196 197 198
        duration.should == subject.send(:run_duration)
      end
    end

    context "events" do
      before :each do
        subject.stubs(:run_duration).returns(10)
      end

      context "resource without events" do
        it "should not include the resource" do
          result = subject.send(:report_to_hash)
199 200
          # the server will populate the report id, so we validate that the
          # client doesn't include one
201
          result.has_key?("report").should be_falsey
202
          result["certname"].should == subject.host
203 204 205
          result["puppet_version"].should == subject.puppet_version
          result["report_format"].should == subject.report_format
          result["configuration_version"].should == subject.configuration_version.to_s
206
          result["resources"].should == []
207
          result["noop"].should == false
208 209 210 211 212
        end
      end

      context "resource with events" do
        it "should include the resource" do
213

214
          event = Puppet::Transaction::Event.new()
215 216 217 218
          event.property = "fooprop"
          event.desired_value = "fooval"
          event.previous_value = "oldfooval"
          event.message = "foomessage"
219 220 221
          if defined?(event.corrective_change) then
            event.corrective_change = true
          end
222
          status.add_event(event)
223

224
          result = subject.send(:report_to_hash)
225 226 227 228 229 230 231 232 233

          result["resources"].length.should == 1
          res = result["resources"][0]
          res["resource_type"].should == "Foo"
          res["resource_title"].should == "foo"
          res["file"].should == "foo"
          res["line"].should == 1
          res["containment_path"].should == ["foo", "bar", "baz"]
          res["events"].length.should == 1
234 235 236 237 238
          if defined?(event.corrective_change) then
            res["corrective_change"].should == true
          else
            res["corrective_change"].should == nil
          end
239 240

          res_event = res["events"][0]
241
          res_event["property"].should == "fooprop"
242 243
          res_event["new_value"].should == "fooval"
          res_event["old_value"].should == "oldfooval"
244
          res_event["message"].should == "foomessage"
245 246 247 248 249
          if defined?(event.corrective_change) then
            res_event["corrective_change"].should == true
          else
            res_event["corrective_change"].should == nil
          end
250 251 252
        end
      end

253
      context "skipped resource status" do
254 255 256
        it "should include the resource" do
          status.skipped = true
          result = subject.send(:report_to_hash)
257 258 259 260 261 262 263

          result["resources"].length.should == 1
          resource = result["resources"][0]
          resource["resource_type"].should == "Foo"
          resource["resource_title"].should == "foo"
          resource["containment_path"].should == ["foo", "bar", "baz"]
          resource["events"].length.should == 0
264 265 266 267 268 269 270 271 272
        end
      end

      context "failed resource status" do
        before :each do
          status.stubs(:failed).returns(true)
        end

        context "with no events" do
273
          it "should have no events" do
274
            result = subject.send(:report_to_hash)
275
            result["resources"].length.should == 0
276 277 278 279 280 281 282 283 284 285 286 287 288
          end
        end

        context "with events" do
          it "should include the actual event" do
            event = Puppet::Transaction::Event.new
            event.property = "barprop"
            event.desired_value = "barval"
            event.previous_value = "oldbarval"
            event.message = "barmessage"
            status.add_event(event)

            result = subject.send(:report_to_hash)
289 290 291 292 293 294 295 296 297 298
            result["resources"].length.should == 1
            resource = result["resources"][0]
            resource["resource_type"].should == "Foo"
            resource["resource_title"].should == "foo"
            resource["file"].should == "foo"
            resource["line"].should == 1
            resource["containment_path"].should == ["foo", "bar", "baz"]
            resource["events"].length.should == 1

            res_event = resource["events"][0]
299
            res_event["property"].should == "barprop"
300 301
            res_event["new_value"].should == "barval"
            res_event["old_value"].should == "oldbarval"
302
            res_event["message"].should == "barmessage"
303 304 305
          end
        end
      end
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326

      context "with unchanged resources turned on" do
        let (:config) {
          Puppet::Util::Puppetdb.config
        }

        before :each do
          config.stubs(:include_unchanged_resources?).returns(true)

          notify_resource =
            stub("notify_resource",
                 { :pathbuilder => ["foo", "bar", "baz"],
                   :path => "foo",
                   :file => "foo",
                   :line => "foo",
                   :tags => [],
                   :type => "Notify",
                   :title => "Hello there" })
          notify_status = Puppet::Resource::Status.new(notify_resource)
          notify_status.changed = false
          subject.add_resource_status(notify_status)
327 328 329 330 331 332 333

          event = Puppet::Transaction::Event.new()
          event.property = "fooprop"
          event.desired_value = "fooval"
          event.previous_value = "oldfooval"
          event.message = "foomessage"
          status.add_event(event)
334 335 336 337 338
        end

        context "with an unchanged resource" do
          it "should include the actual event" do
            result = subject.send(:report_to_hash)
339 340 341
            unchanged_resources = result["resources"].select { |res| res["events"].empty? and ! (res["skipped"])}
            unchanged_resources.length.should == 1
            resource = unchanged_resources[0]
342 343 344 345 346 347 348 349 350 351 352
            resource["resource_type"].should == "Notify"
            resource["resource_title"].should == "Hello there"
            resource["file"].should == "foo"
            resource["line"].should == "foo"
            resource["containment_path"].should == ["foo", "bar", "baz"]
            resource["events"].length.should == 0
          end
        end

      end

353 354 355
    end
  end
end