fix a few issues with pipeline source loading

- allow empty config.string
- move all config autocompletion logic to the ConfigStringLoader
- gracefully handle absense of files in path.config
- ensure original_settings are restored in multi_local source after PipelineConfig creation

Fixes #7866
This commit is contained in:
Joao Duarte 2017-08-01 10:52:16 +01:00 committed by João Duarte
parent c5e25cf6fa
commit cb2f01b4b6
5 changed files with 38 additions and 28 deletions

View file

@ -41,7 +41,7 @@ module LogStash module Config module Source
end
def config_string?
!(config_string.nil? || config_string.empty?)
!config_string.nil?
end
def config_path_setting

View file

@ -18,8 +18,27 @@ module LogStash module Config module Source
#
class Local < Base
class ConfigStringLoader
INPUT_BLOCK_RE = /input *{/
OUTPUT_BLOCK_RE = /output *{/
EMPTY_RE = /^\s*$/
def self.read(config_string)
[org.logstash.common.SourceWithMetadata.new("string", "config_string", 0, 0, config_string)]
config_parts = [org.logstash.common.SourceWithMetadata.new("string", "config_string", 0, 0, config_string)]
# Make sure we have an input and at least 1 output
# if its not the case we will add stdin and stdout
# this is for backward compatibility reason
if !INPUT_BLOCK_RE.match(config_string)
config_parts << org.logstash.common.SourceWithMetadata.new(self.class.name, "default input", 0, 0, LogStash::Config::Defaults.input)
end
# include a default stdout output if no outputs given
if !OUTPUT_BLOCK_RE.match(config_string)
config_parts << org.logstash.common.SourceWithMetadata.new(self.class.name, "default output", 0, 0, LogStash::Config::Defaults.output)
end
config_parts
end
end
@ -135,8 +154,6 @@ module LogStash module Config module Source
PIPELINE_ID = LogStash::SETTINGS.get("pipeline.id").to_sym
HTTP_RE = /^http(s)?/
INPUT_BLOCK_RE = /input *{/
OUTPUT_BLOCK_RE = /output *{/
def pipeline_configs
if config_conflict?
@ -178,9 +195,7 @@ module LogStash module Config module Source
[]
end
return if config_parts.empty?
add_missing_default_inputs_or_outputs(config_parts) if config_string?
return [] if config_parts.empty?
[PipelineConfig.new(self.class, @settings.get("pipeline.id").to_sym, config_parts, @settings)]
end
@ -189,20 +204,6 @@ module LogStash module Config module Source
config_reload_automatic? && !config_path? && config_string?
end
# Make sure we have an input and at least 1 output
# if its not the case we will add stdin and stdout
# this is for backward compatibility reason
def add_missing_default_inputs_or_outputs(config_parts)
if !config_parts.any? { |part| INPUT_BLOCK_RE.match(part.text) }
config_parts << org.logstash.common.SourceWithMetadata.new(self.class.name, "default input", 0, 0, LogStash::Config::Defaults.input)
end
# include a default stdout output if no outputs given
if !config_parts.any? { |part| OUTPUT_BLOCK_RE.match(part.text) }
config_parts << org.logstash.common.SourceWithMetadata.new(self.class.name, "default output", 0, 0, LogStash::Config::Defaults.output)
end
end
def local_config?
return false unless config_path?

View file

@ -19,13 +19,15 @@ module LogStash module Config module Source
::LogStash::PipelineSettings.from_settings(@original_settings.clone).merge(pipeline_settings)
end
detect_duplicate_pipelines(pipelines_settings)
pipelines_settings.map do |pipeline_settings|
pipeline_configs = pipelines_settings.map do |pipeline_settings|
@settings = pipeline_settings
# this relies on instance variable @settings and the parent class' pipeline_configs
# method. The alternative is to refactor most of the Local source methods to accept
# a settings object instead of relying on @settings.
local_pipeline_configs # create a PipelineConfig object based on @settings
end.flatten
@settings = @original_settings
pipeline_configs
end
def match?

View file

@ -60,6 +60,10 @@ public class SourceWithMetadata implements HashableWithSource {
return false;
}).collect(Collectors.toList());
if (!(this.getText() instanceof String)) {
badAttributes.add(this.getText());
}
if (!badAttributes.isEmpty()){
String message = "Missing attributes in SourceWithMetadata: (" + badAttributes + ") "
+ this.toString();
@ -72,7 +76,7 @@ public class SourceWithMetadata implements HashableWithSource {
}
public int hashCode() {
return Objects.hash(attributes().toArray());
return Objects.hash(hashableAttributes().toArray());
}
public String toString() {
@ -81,11 +85,16 @@ public class SourceWithMetadata implements HashableWithSource {
@Override
public String hashSource() {
return attributes().stream().map(Object::toString).collect(Collectors.joining("|"));
return hashableAttributes().stream().map(Object::toString).collect(Collectors.joining("|"));
}
// Fields checked for being not null and non empty String
private Collection<Object> attributes() {
return Arrays.asList(this.getId(), this.getProtocol(), this.getLine(), this.getColumn());
}
// Fields used in the hashSource and hashCode methods to ensure uniqueness
private Collection<Object> attributes() {
private Collection<Object> hashableAttributes() {
return Arrays.asList(this.getId(), this.getProtocol(), this.getLine(), this.getColumn(), this.getText());
}
}

View file

@ -40,10 +40,8 @@ public class SourceWithMetadataTest {
new ParameterGroup("proto", "path", 1, 1, null),
new ParameterGroup("", "path", 1, 1, "foo"),
new ParameterGroup("proto", "", 1, 1, "foo"),
new ParameterGroup("proto", "path", 1, 1, ""),
new ParameterGroup(" ", "path", 1, 1, "foo"),
new ParameterGroup("proto", " ", 1, 1, "foo"),
new ParameterGroup("proto", "path", 1, 1, " ")
new ParameterGroup("proto", " ", 1, 1, "foo")
);
}