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
140
|
#
# 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)
require "chef-utils/dist"
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?)
unless valid_hash_with_keys?(output_locations, :urls, :files)
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|
Array(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 #{ChefUtils::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] && !valid_hash_with_keys?(Chef::Config[:data_collector][:output_locations], :urls)
# we can run fine to a file without a token, even in solo mode.
unless valid_hash_with_keys?(Chef::Config[:data_collector][:output_locations], :files)
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
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 #{ChefUtils::Dist::Automate::PRODUCT} data collector with #{ChefUtils::Dist::Solo::PRODUCT}")
false
else
true
end
end
private
# validate an output_location file
def validate_file!(file)
return true if Chef::Config.path_accessible?(File.expand_path(file))
raise Chef::Exceptions::ConfigurationError,
"Chef::Config[:data_collector][:output_locations][:files] contains the location #{file}, which is a non existent file path."
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
# Validate the hash contains at least one of the given keys.
#
# @param hash [Hash] the hash to be validated.
# @param keys [Array] an array of keys to check existence of in the hash.
# @return [Boolean] true if the hash contains any of the given keys.
#
def valid_hash_with_keys?(hash, *keys)
hash.is_a?(Hash) && keys.any? { |k| hash.key?(k) }
end
end
end
end
end
|