summaryrefslogtreecommitdiff
path: root/lib/chef/data_collector/resource_report.rb
blob: 6d63595cc2b3e4e3d092989502e1e998cc395917 (plain)
1
2
3
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
#
# Author:: Adam Leff (<adamleff@chef.io>)
# Author:: Ryan Cragun (<ryan@chef.io>)
#
# Copyright:: Copyright 2012-2018, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "chef/exceptions"

class Chef
  class DataCollector
    class ResourceReport

      attr_reader :action, :elapsed_time, :new_resource, :status
      attr_accessor :conditional, :current_resource, :exception

      def initialize(new_resource, action, current_resource = nil)
        @new_resource     = new_resource
        @action           = action
        @current_resource = current_resource
        @status           = "unprocessed"
      end

      def skipped(conditional)
        @status      = "skipped"
        @conditional = conditional
      end

      def updated
        @status = "updated"
      end

      def failed(exception)
        @current_resource = nil
        @status           = "failed"
        @exception        = exception
      end

      def up_to_date
        @status = "up-to-date"
      end

      def finish
        @elapsed_time = new_resource.elapsed_time
      end

      def elapsed_time_in_milliseconds
        elapsed_time.nil? ? nil : (elapsed_time * 1000).to_i
      end

      def potentially_changed?
        %w{updated failed}.include?(status)
      end

      def to_h
        hash = {
          "type"           => new_resource.resource_name.to_sym,
          "name"           => new_resource.name.to_s,
          "id"             => resource_identity,
          "after"          => new_resource_state_reporter,
          "before"         => current_resource_state_reporter,
          "duration"       => elapsed_time_in_milliseconds.to_s,
          "delta"          => new_resource.respond_to?(:diff) && potentially_changed? ? new_resource.diff : "",
          "ignore_failure" => new_resource.ignore_failure,
          "result"         => action.to_s,
          "status"         => status,
        }

        if new_resource.cookbook_name
          hash["cookbook_name"]    = new_resource.cookbook_name
          hash["cookbook_version"] = new_resource.cookbook_version.version
          hash["recipe_name"]      = new_resource.recipe_name
        end

        hash["conditional"] = conditional.to_text if status == "skipped"
        hash["error_message"] = exception.message unless exception.nil?

        hash
      end
      alias_method :to_hash, :to_h
      alias_method :for_json, :to_h

      # We should be able to call the identity of a resource safely, but there
      # is an edge case where resources that have a lazy property that is both
      # the name_property and the identity property, it will thow a validation
      # exception causing the chef-client run to fail. We are not fixing this
      # case since Chef is actually doing the right thing but we are making the
      # ResourceReporter smarter so that it detects the failure and sends a
      # message to the data collector containing a static resource identity
      # since we were unable to generate a proper one.
      def resource_identity
        new_resource.identity.to_s
      rescue => e
        "unknown identity (due to #{e.class})"
      end

      def new_resource_state_reporter
        new_resource.state_for_resource_reporter
      rescue
        {}
      end

      def current_resource_state_reporter
        current_resource ? current_resource.state_for_resource_reporter : {}
      rescue
        {}
      end
    end
  end
end