summaryrefslogtreecommitdiff
path: root/lib/chef/data_collector/config_validation.rb
blob: 1a7940b0ae83cfbb4537ccb376d993277c58472c (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
#
# Copyright:: Copyright (c) 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 "uri" unless defined?(URI)

class Chef
  class DataCollector

    # @api private
    module ConfigValidation
      class << self
        def validate_server_url!
          # if we have a server_url set we ALWAYS validate it, and we MUST have an output_location set to skip server_url validation
          # (having output_locations set and no server_url is valid, but both of them unset blows up in here)
          return if !Chef::Config[:data_collector][:server_url] && Chef::Config[:data_collector][:output_locations]

          begin
            uri = URI(Chef::Config[:data_collector][:server_url])
          rescue
            raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{Chef::Config[:data_collector][:server_url]}) is not a valid URI."
          end

          if uri.host.nil?
            raise Chef::Exceptions::ConfigurationError,
              "Chef::Config[:data_collector][:server_url] (#{Chef::Config[:data_collector][:server_url]}) is a URI with no host. Please supply a valid URL."
          end
        end

        def validate_output_locations!
          # not having an output_location set at all is fine, we just skip it then
          output_locations = Chef::Config[:data_collector][:output_locations]
          return unless output_locations

          # but deliberately setting an empty output_location we consider to be an error (XXX: but should we?)
          if output_locations.empty?
            raise Chef::Exceptions::ConfigurationError,
              "Chef::Config[:data_collector][:output_locations] is empty. Please supply an hash of valid URLs and / or local file paths."
          end

          # loop through all the types and locations and validate each one-by-one
          output_locations.each do |type, locations|
            locations.each do |location|
              validate_url!(location) if type == :urls
              validate_file!(location) if type == :files
            end
          end
        end

        # Main logic controlling the data collector being enabled or disabled:
        #
        # * disabled in why-run mode
        # * disabled when `Chef::Config[:data_collector][:mode]` excludes the solo-vs-client mode
        # * disabled if there is no server_url or no output_locations to log to
        # * enabled if there is a configured output_location even without a token
        # * disabled in solo mode if the user did not configure the auth token
        #
        # @return [Boolean] true if the data collector should be enabled
        #
        def should_be_enabled?
          running_mode = ( Chef::Config[:solo_legacy_mode] || Chef::Config[:local_mode] ) ? :solo : :client
          want_mode = Chef::Config[:data_collector][:mode]

          case
          when Chef::Config[:why_run]
            Chef::Log.trace("data collector is disabled for why run mode")
            false
          when (want_mode != :both) && running_mode != want_mode
            Chef::Log.trace("data collector is configured to only run in #{Chef::Config[:data_collector][:mode]} modes, disabling it")
            false
          when !(Chef::Config[:data_collector][:server_url] || Chef::Config[:data_collector][:output_locations])
            Chef::Log.trace("Neither data collector URL or output locations have been configured, disabling data collector")
            false
          when running_mode == :client && Chef::Config[:data_collector][:token]
            Chef::Log.warn("Data collector token authentication is not recommended for client-server mode. " \
                           "Please upgrade #{Chef::Dist::SERVER_PRODUCT} to 12.11 or later and remove the token from your config file " \
                           "to use key based authentication instead")
            true
          when Chef::Config[:data_collector][:output_locations] && Chef::Config[:data_collector][:output_locations][:files] && !Chef::Config[:data_collector][:output_locations][:files].empty?
            # we can run fine to a file without a token, even in solo mode.
            true
          when running_mode == :solo && !Chef::Config[:data_collector][:token]
            # we are in solo mode and are not logging to a file, so must have a token
            Chef::Log.trace("Data collector token must be configured to use #{Chef::Dist::AUTOMATE} data collector with #{Chef::Dist::SOLO}")
            false
          else
            true
          end
        end

        private

        # validate an output_location file
        def validate_file!(file)
          open(file, "a") {}
        rescue Errno::ENOENT
          raise Chef::Exceptions::ConfigurationError,
            "Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which is a non existent file path."
        rescue Errno::EACCES
          raise Chef::Exceptions::ConfigurationError,
            "Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which cannot be written to by Chef."
        rescue Exception => e
          raise Chef::Exceptions::ConfigurationError,
            "Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which is invalid: #{e.message}."
        end

        # validate an output_location url
        def validate_url!(url)
          URI(url)
        rescue
          raise Chef::Exceptions::ConfigurationError,
            "Chef::Config[:data_collector][:output_locations][:urls] contains the url #{url} which is not valid."
        end

      end
    end
  end
end