Search code examples
ruby-on-railsrubypuppetflume

Puppet .erb template: syntax error, unexpected $end, expecting ')' (or: inserting variables declared in .erb into text after end of code)


I'm working on an .erb in Puppet to manage an Apache Flume config file. (Flume is going to ship logs to Elasticsearch, so they can be searched and sorted in Kibana.)

Some background: it's getting this information from a custom fact in facter called logpaths that keeps the paths of my logs in this format:

>MysqlBlah;/var/logs/mysql/error.log:/var/logs/mysql/slow_query.log
>TomcatBlah;/var/logs/tomcat/catalina.out:/var/logs/tomcat/error.log

So each service starts with a >, the list of logs starts with a ;, and the log paths are delimited with a :.

I want my ERB to read that, and iterate through each logfile to come out with text I can put in my flume config file.

All that seems to be OK, but I'm getting hit with an error that I can't track down:

-:58: syntax error, unexpected $end, expecting ')'

I don't seem to have any unclosed parentheses anywhere, so I'm not sure how to look for what's causing this.

EDIT 1:

Per @Daiku, I added double-quotes around all the variables, and it is now passing syntax check. However, Puppet now presents this error:

Could not find value for 'sourcenamelist'

Is this a scope issue? Is there another way to access the variable (which I declared in this erb) after the %>?

EDIT 2:

I should have looked at the line number. It turns out I could insert variables the way I was, the line number causing problems was the one where I was appending to a list (which makes sense, as I hadn't declared that variable before I started appending to it). I'm back on track now; will post the finished code once I work out the remaining bugs of mine.

My code is as follows:

<%
#split on > to get each service
services = @logpaths.split(">")
services.each do |serviceline| 
#split on semicolon
service = serviceline.split(";")
servicename = service[0]
#split into logs on colon
logs = service[1].string.split(":")
lognumber = 0
logs.each do |log|
#sourcelist is a space-delimited list of names for each logfile (eg mysql3311-1)
lognumber += 1
sourcename = servicename + "-" + lognumber.to_s + " "
sourcenamelist << sourcename    
#sourceconfigs assigns each source to channel, exec path
sourceconfig = "a1.sources.#{sourcename}.type = exec\na1.sources.#{sourcename}.command = tail -F #{log}\na1.sources.#{sourcename}.channels = c1"
sourceconfiglist << sourceconfig
#sourceinterceptors appends sourcename, path, and servicename (you know, for search)
sourceinterceptors = "a1.sources.#{sourcename}.interceptors = i1 i2 i3 i4\na1.sources.#{sourcename}.interceptors.i1.type = timestamp"
sourceinterceptorlist << sourceinterceptors
end
end 
-%>

# Name the components on this agent
a1.sources = <%= #{sourcenamelist} -%>
a1.sinks = k1
a1.channels = c1

# Describe/configure the source 
<%= #{sourceconfiglist} -%>

# Describe the sink
a1.sinks.k1.type = elasticsearch
a1.sinks.k1.hostNames = (my elasticsearch host):9300
a1.sinks.k1.indexName = flume
a1.sinks.k1.indexType = flume_logs
a1.sinks.k1.clusterName = elasticsearch
a1.sinks.k1.batchSize = 1000
a1.sinks.k1.ttl = 2
a1.sinks.k1.serializer = org.apache.flume.sink.elasticsearch.ElasticSearchLogStashEventSerializer

# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

#interceptor
<%= #{sourceinterceptorlist} -%>

Solution

  • You need double-quotes around your interpolated ruby stuff:

    a1.sources = <%= "#{sourcenamelist}" -%>

    etc.