summaryrefslogtreecommitdiff
path: root/lib/chef/resource/file/verification.rb
blob: ae4f9043b2f01f4c01bb89ec61eaa382c45c49c7 (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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#
# Author:: Steven Danna (<steve@chef.io>)
# Copyright:: Copyright 2014-2016, 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"
require "chef/guard_interpreter"
require "chef/mixin/descendants_tracker"

class Chef
  class Resource
    class File < Chef::Resource

      #
      # See RFC 027 for a full specification
      #
      # File verifications allow user-supplied commands a means of
      # preventing file resource content deploys.  Their intended use
      # is to verify the contents of a temporary file before it is
      # deployed onto the system.
      #
      # Similar to not_if and only_if, file verifications can take a
      # ruby block, which will be called, or a string, which will be
      # executed as a Shell command.
      #
      # Additonally, Chef or third-party verifications can ship
      # "registered verifications" that the user can use by specifying
      # a :symbol as the command name.
      #
      # To create a registered verification, create a class that
      # inherits from Chef::Resource::File::Verification and use the
      # provides class method to give it name.  Registered
      # verifications are expected to supply a verify instance method
      # that takes 2 arguments.
      #
      # Example:
      # class Chef
      #  class Resource
      #    class File::Verification::Foo < Chef::Resource::File::Verification
      #      provides :noop
      #      def verify(path, opts)
      #        #yolo
      #        true
      #      end
      #    end
      #  end
      # end
      #
      #

      class Verification
        extend Chef::Mixin::DescendantsTracker

        def self.provides(name)
          @provides = name
        end

        def self.provides?(name)
          @provides == name
        end

        def self.lookup(name)
          c = descendants.find { |d| d.provides?(name) }
          if c.nil?
            raise Chef::Exceptions::VerificationNotFound.new "No file verification for #{name} found."
          end
          c
        end

        def logger
          @parent_resource.logger
        end

        def initialize(parent_resource, command, opts, &block)
          @command, @command_opts = command, opts
          @block = block
          @parent_resource = parent_resource
        end

        def verify(path, opts = {})
          logger.trace("Running verification[#{self}] on #{path}")
          if @block
            verify_block(path, opts)
          elsif @command.is_a?(Symbol)
            verify_registered_verification(path, opts)
          elsif @command.is_a?(String)
            verify_command(path, opts)
          end
        end

        # opts is currently unused, but included in the API
        # to support future extensions
        def verify_block(path, opts)
          @block.call(path)
        end

        # We reuse Chef::GuardInterpreter in order to support
        # the same set of options that the not_if/only_if blocks do
        def verify_command(path, opts)
          if @command.include?("%{file}")
            raise ArgumentError, "The %{file} expansion for verify commands has been removed. Please use %{path} instead."
          end
          command = @command % { path: path }
          interpreter = Chef::GuardInterpreter.for_resource(@parent_resource, command, @command_opts)
          interpreter.evaluate
        end

        def verify_registered_verification(path, opts)
          verification_class = Chef::Resource::File::Verification.lookup(@command)
          v = verification_class.new(@parent_resource, @command, @command_opts, &@block)
          v.verify(path, opts)
        end

        def to_s
          if @block
            "<Proc>"
          elsif @command.is_a?(Symbol)
            "#{@command.inspect} (#{Chef::Resource::File::Verification.lookup(@command).name})"
          elsif @command.is_a?(String)
            @command
          end
        end
      end
    end
  end
end