jerakia 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hiera/backend/jerakia_backend.rb +13 -14
  3. data/lib/jerakia/answer.rb +28 -27
  4. data/lib/jerakia/cache/entry.rb +2 -6
  5. data/lib/jerakia/cache/file.rb +53 -23
  6. data/lib/jerakia/cache.rb +44 -11
  7. data/lib/jerakia/cli/lookup.rb +124 -0
  8. data/lib/jerakia/cli/server.rb +50 -0
  9. data/lib/jerakia/cli/token.rb +64 -0
  10. data/lib/jerakia/cli.rb +7 -117
  11. data/lib/jerakia/config.rb +5 -5
  12. data/lib/jerakia/datasource/dummy.rb +1 -6
  13. data/lib/jerakia/datasource/file/json.rb +1 -3
  14. data/lib/jerakia/datasource/file/yaml.rb +1 -3
  15. data/lib/jerakia/datasource/file.rb +21 -44
  16. data/lib/jerakia/datasource/http.rb +17 -23
  17. data/lib/jerakia/datasource.rb +37 -36
  18. data/lib/jerakia/dsl/lookup.rb +4 -6
  19. data/lib/jerakia/dsl/policy.rb +11 -12
  20. data/lib/jerakia/error.rb +0 -5
  21. data/lib/jerakia/launcher.rb +26 -30
  22. data/lib/jerakia/log.rb +21 -22
  23. data/lib/jerakia/lookup/plugin/hiera.rb +3 -4
  24. data/lib/jerakia/lookup/plugin.rb +5 -6
  25. data/lib/jerakia/lookup/plugin_config.rb +31 -0
  26. data/lib/jerakia/lookup/pluginfactory.rb +30 -36
  27. data/lib/jerakia/lookup.rb +31 -32
  28. data/lib/jerakia/policy.rb +60 -45
  29. data/lib/jerakia/request.rb +3 -2
  30. data/lib/jerakia/response/filter/encryption.rb +7 -12
  31. data/lib/jerakia/response/filter/strsub.rb +4 -9
  32. data/lib/jerakia/response/filter.rb +5 -5
  33. data/lib/jerakia/response.rb +7 -13
  34. data/lib/jerakia/schema.rb +23 -35
  35. data/lib/jerakia/scope/metadata.rb +0 -1
  36. data/lib/jerakia/scope/puppetdb.rb +38 -0
  37. data/lib/jerakia/scope/server.rb +60 -0
  38. data/lib/jerakia/scope/yaml.rb +3 -4
  39. data/lib/jerakia/scope.rb +0 -2
  40. data/lib/jerakia/server/auth/token.rb +35 -0
  41. data/lib/jerakia/server/auth.rb +72 -0
  42. data/lib/jerakia/server/rest.rb +140 -0
  43. data/lib/jerakia/server.rb +41 -0
  44. data/lib/jerakia/util.rb +6 -7
  45. data/lib/jerakia/version.rb +1 -3
  46. data/lib/jerakia.rb +58 -40
  47. data/lib/puppet/indirector/data_binding/jerakia.rb +9 -11
  48. data/lib/puppet/indirector/data_binding/jerakia_rest.rb +11 -13
  49. metadata +78 -11
data/lib/jerakia/log.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  class Jerakia::Log < Jerakia
2
-
3
2
  require 'logger'
4
- def initialize(level=:info,file='/var/log/jerakia.log')
3
+ def initialize(level = :info, file = '/var/log/jerakia.log')
5
4
  begin
6
5
  @@logger ||= Logger.new(file)
7
6
  rescue Errno::EACCES => e
@@ -19,6 +18,10 @@ class Jerakia::Log < Jerakia
19
18
  end
20
19
  end
21
20
 
21
+ def logger
22
+ @@logger
23
+ end
24
+
22
25
  def verbose(msg)
23
26
  @@logger.info msg if @@level == :verbose
24
27
  end
@@ -39,24 +42,20 @@ class Jerakia::Log < Jerakia
39
42
  @@logger.fatal msg
40
43
  end
41
44
 
42
- # def self.fatal(msg)
43
- # self.new.fatal msg
44
- # end
45
- #
46
- # def self.error(msg)
47
- # self.new.error msg
48
- # end
49
- #
50
- # def self.debug(msg)
51
- # self.new.debug msg
52
- # end
53
- #
54
- ## def self.info(msg)
55
- # puts @@logger
56
- # self.new.info msg
57
- # end
58
-
59
-
60
-
45
+ # def self.fatal(msg)
46
+ # self.new.fatal msg
47
+ # end
48
+ #
49
+ # def self.error(msg)
50
+ # self.new.error msg
51
+ # end
52
+ #
53
+ # def self.debug(msg)
54
+ # self.new.debug msg
55
+ # end
56
+ #
57
+ ## def self.info(msg)
58
+ # puts @@logger
59
+ # self.new.info msg
60
+ # end
61
61
  end
62
-
@@ -8,16 +8,15 @@
8
8
  #
9
9
  class Jerakia::Lookup::Plugin
10
10
  module Hiera
11
-
12
11
  def autorun
13
- if request.namespace.length > 0
12
+ unless request.namespace.empty?
14
13
  request.key.prepend("#{request.namespace.join('::')}::")
15
14
  end
16
- request.namespace=[]
15
+ request.namespace = []
17
16
  end
18
17
 
19
18
  def rewrite_lookup
20
- Jerakia.log.debug("DEPRECATION NOTICE: The use of plugin.hiera.rewrite_lookup is now deprecated and is automatically executed when the plugin is loaded")
19
+ Jerakia.log.debug('DEPRECATION NOTICE: The use of plugin.hiera.rewrite_lookup is now deprecated and is automatically executed when the plugin is loaded')
21
20
  end
22
21
 
23
22
  def calling_module
@@ -1,16 +1,17 @@
1
1
  class Jerakia::Lookup::Plugin
2
-
3
-
4
2
  attr_reader :lookup
5
-
6
- def initialize(lookup)
3
+ attr_reader :config
4
+
5
+ def initialize(lookup, config)
7
6
  @lookup = lookup
7
+ @config = config
8
8
  end
9
9
 
10
10
  def activate(name)
11
11
  instance_eval "extend Jerakia::Lookup::Plugin::#{name.to_s.capitalize}"
12
12
  end
13
13
 
14
+
14
15
  def scope
15
16
  lookup.scope
16
17
  end
@@ -18,6 +19,4 @@ class Jerakia::Lookup::Plugin
18
19
  def request
19
20
  lookup.request
20
21
  end
21
-
22
22
  end
23
-
@@ -0,0 +1,31 @@
1
+ # Jerakia::Lookup::PluginConfig
2
+ #
3
+ # This class is a simple wrapper class to expose configuration options
4
+ # from the global configuration file to lookup plugins. It's exposed
5
+ # to the lookup as the config method. Eg: config[:foo]
6
+ #
7
+ class Jerakia
8
+ class Lookup
9
+ class PluginConfig
10
+
11
+ attr_reader :plugin_name
12
+ attr_reader :config
13
+
14
+ def initialize(plugin_name)
15
+ @plugin_name = plugin_name
16
+ @config = {}
17
+ if Jerakia.config[:plugins].is_a?(Hash)
18
+ @config = Jerakia.config[:plugins][plugin_name.to_s] || {}
19
+ end
20
+ end
21
+
22
+ def [](key)
23
+ config[key.to_s]
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
30
+
31
+
@@ -1,38 +1,32 @@
1
1
  class Jerakia::Lookup::PluginFactory
2
-
3
- def initialize
4
- Jerakia.log.debug("Loaded plugin handler")
5
- @plugin_config = Jerakia.config[:plugins] || {}
6
- end
7
-
8
-
9
- def create_plugin_method(name, &block)
10
- self.class.send(:define_method, name, &block)
11
- end
12
-
13
- def register(name,plugin)
14
- begin
15
- require "jerakia/lookup/plugin/#{name}"
16
- rescue LoadError => e
17
- raise Jerakia::Error, "Cannot load plugin #{name}, #{e.message}"
18
- end
19
-
20
-
21
- plugin.activate(name)
22
- create_plugin_method(name) do
23
- plugin
24
- end
25
- if plugin.respond_to?('autorun')
26
- Jerakia.log.debug("Found autorun method for plugin #{name}, executing")
27
-
28
- if plugin.method('autorun').arity == 1
29
- plugin.autorun (@plugin_config[name.to_s] || {} )
30
- else
31
- plugin.autorun
32
- end
33
- end
34
- end
35
-
2
+ def initialize
3
+ Jerakia.log.debug('Loaded plugin handler')
4
+ @plugin_config = Jerakia.config[:plugins] || {}
5
+ end
6
+
7
+ def create_plugin_method(name, &block)
8
+ self.class.send(:define_method, name, &block)
9
+ end
10
+
11
+ def register(name, plugin)
12
+ begin
13
+ require "jerakia/lookup/plugin/#{name}"
14
+ rescue LoadError => e
15
+ raise Jerakia::Error, "Cannot load plugin #{name}, #{e.message}"
16
+ end
17
+
18
+ plugin.activate(name)
19
+ create_plugin_method(name) do
20
+ plugin
21
+ end
22
+ if plugin.respond_to?('autorun')
23
+ Jerakia.log.debug("Found autorun method for plugin #{name}, executing")
24
+
25
+ if plugin.method('autorun').arity == 1
26
+ plugin.autorun (@plugin_config[name.to_s] || {})
27
+ else
28
+ plugin.autorun
29
+ end
30
+ end
31
+ end
36
32
  end
37
-
38
-
@@ -3,6 +3,7 @@ class Jerakia::Lookup
3
3
  require 'jerakia/scope'
4
4
  require 'jerakia/lookup/plugin'
5
5
  require 'jerakia/lookup/pluginfactory'
6
+ require 'jerakia/lookup/plugin_config'
6
7
 
7
8
  attr_accessor :request
8
9
  attr_accessor :datasource
@@ -15,20 +16,18 @@ class Jerakia::Lookup
15
16
  attr_reader :pluginfactory
16
17
  attr_reader :datasource
17
18
 
18
- def initialize(name,opts,req,scope)
19
-
20
- @name=name
21
- @request=req
22
- @valid=true
23
- @scope_object=scope
24
- @output_filters=[]
25
- @proceed=true
19
+ def initialize(name, opts, req, scope)
20
+ @name = name
21
+ @request = req
22
+ @valid = true
23
+ @scope_object = scope
24
+ @output_filters = []
25
+ @proceed = true
26
26
  @pluginfactory = Jerakia::Lookup::PluginFactory.new
27
27
 
28
-
29
28
  # Validate options passed to the lookup
30
29
  #
31
- valid_opts = [ :use ]
30
+ valid_opts = [:use]
32
31
 
33
32
  opts.keys.each do |opt_key|
34
33
  unless valid_opts.include?(opt_key)
@@ -36,17 +35,23 @@ class Jerakia::Lookup
36
35
  end
37
36
  end
38
37
 
39
-
40
38
  if opts[:use]
41
- Array(opts[:use]).flatten.each do |plugin|
39
+ Array(opts[:use]).flatten.each do |plugin|
42
40
  plugin_load(plugin)
43
41
  end
44
42
  end
45
43
  end
46
-
44
+
45
+ # Retrieve plugin specific configuration from the global configuration file
46
+ # gets passed to the plugin instance upon initilization.
47
+ #
48
+ def plugin_config(plugin)
49
+ Jerakia::Lookup::PluginConfig.new(plugin)
50
+ end
51
+
47
52
  def plugin_load(plugin)
48
53
  Jerakia.log.debug("Loading plugin #{plugin}")
49
- pluginfactory.register(plugin, Jerakia::Lookup::Plugin.new(self))
54
+ pluginfactory.register(plugin, Jerakia::Lookup::Plugin.new(self, plugin_config(plugin)))
50
55
  end
51
56
 
52
57
  def plugin
@@ -57,7 +62,7 @@ class Jerakia::Lookup
57
62
  @datasource
58
63
  end
59
64
 
60
- def datasource(source, opts={})
65
+ def datasource(source, opts = {})
61
66
  @datasource = Jerakia::Datasource.new(source, self, opts)
62
67
  end
63
68
 
@@ -69,11 +74,10 @@ class Jerakia::Lookup
69
74
  scope_object.value
70
75
  end
71
76
 
72
-
73
- def output_filter(name,opts={})
77
+ def output_filter(name, opts = {})
74
78
  @output_filters << { :name => name, :opts => opts }
75
79
  end
76
-
80
+
77
81
  def proceed?
78
82
  proceed
79
83
  end
@@ -109,38 +113,33 @@ class Jerakia::Lookup
109
113
  valid
110
114
  end
111
115
 
112
-
113
- def get_matches(key,match)
114
- matches = Array(match).select { |m| key[Regexp.new(m)] == key}
116
+ def get_matches(key, match)
117
+ matches = Array(match).select { |m| key[Regexp.new(m)] == key }
115
118
  end
116
-
117
- def confine(key=nil,match)
119
+
120
+ def confine(key = nil, match)
118
121
  if key
119
- invalidate unless get_matches(key,match).size > 0
122
+ invalidate if get_matches(key, match).empty?
120
123
  else
121
124
  invalidate
122
125
  end
123
126
  end
124
127
 
125
- def exclude(key=nil,match)
128
+ def exclude(key = nil, match)
126
129
  if key
127
- invalidate if get_matches(key,match).size > 0
130
+ invalidate unless get_matches(key, match).empty?
128
131
  end
129
132
  end
130
-
131
133
 
132
134
  def run
133
135
  Jerakia.log.verbose("lookup: #{@name} key: #{@request.key} namespace: #{@request.namespace.join('/')}")
134
136
  @datasource.run
135
- response=@datasource.response
137
+ response = @datasource.response
136
138
  @output_filters.each do |filter|
137
139
  response.filter! filter[:name], filter[:opts]
138
140
  end
139
- return response
141
+ response
140
142
  end
141
143
 
142
-
143
144
  private
144
-
145
145
  end
146
-
@@ -2,69 +2,84 @@ require 'jerakia/launcher'
2
2
  require 'jerakia/answer'
3
3
  require 'jerakia/schema'
4
4
 
5
- class Jerakia::Policy
5
+ class Jerakia
6
+ class Policy
7
+ attr_accessor :lookups
8
+ attr_reader :answer
9
+ attr_reader :scope
10
+ attr_reader :lookup_proceed
11
+ attr_reader :schema
12
+ attr_reader :request
6
13
 
7
- attr_accessor :lookups
8
- attr_reader :answer
9
- attr_reader :scope
10
- attr_reader :lookup_proceed
11
- attr_reader :schema
12
- attr_reader :request
13
-
14
- def initialize(name, opts={}, req)
14
+ # _opts currently does not get used, but is included here as a placeholder
15
+ # for allowing policies to be declared with options;
16
+ # policy :foo, :option => :value do
17
+ #
18
+ def initialize(_name, _opts, req)
19
+ if req.use_schema && Jerakia.config[:enable_schema]
20
+ schema_config = Jerakia.config[:schema] || {}
21
+ @schema = Jerakia::Schema.new(req, schema_config)
22
+ end
15
23
 
16
- if req.use_schema and Jerakia.config[:enable_schema]
17
- schema_config = Jerakia.config[:schema] || {}
18
- @schema = Jerakia::Schema.new(req, schema_config)
24
+ @lookups = []
25
+ @request = req
26
+ @answer = Jerakia::Answer.new(req.lookup_type)
27
+ @scope = Jerakia::Scope.new(req)
28
+ @lookup_proceed = true
19
29
  end
20
30
 
31
+ def clone_request
32
+ Marshal.load(Marshal.dump(request))
33
+ end
21
34
 
22
- @lookups=[]
23
- @request=req
24
- @answer=Jerakia::Answer.new(req.lookup_type)
25
- @scope=Jerakia::Scope.new(req)
26
- @lookup_proceed = true
27
- end
28
-
29
- def clone_request
30
- Marshal.load(Marshal.dump(request))
31
- end
35
+ def submit_lookup(lookup)
36
+ raise Jerakia::PolicyError, "Lookup #{lookup.name} has no datasource defined" unless lookup.get_datasource
37
+ @lookups << lookup if lookup.valid? && @lookup_proceed
38
+ @lookup_proceed = false if !lookup.proceed? && lookup.valid?
39
+ end
32
40
 
33
- def submit_lookup(lookup)
34
- raise Jerakia::PolicyError, "Lookup #{lookup.name} has no datasource defined" unless lookup.get_datasource
35
- @lookups << lookup if lookup.valid? and @lookup_proceed
36
- @lookup_proceed = false if !lookup.proceed? and lookup.valid?
37
- end
41
+ def execute
42
+ response_entries = []
38
43
 
39
- def fire!
40
- response_entries = []
44
+ @lookups.each do |l|
45
+ responses = l.run
46
+ lookup_answers = responses.entries.map { |r| r }
47
+ response_entries << lookup_answers if lookup_answers
48
+ end
41
49
 
42
- @lookups.each do |l|
43
- responses = l.run
44
- lookup_answers = responses.entries.map { |r| r }
45
- response_entries << lookup_answers if lookup_answers
50
+ response_entries.flatten.each { |res| process_response(res) }
51
+ consolidate_answer
46
52
  end
47
53
 
54
+ private
48
55
 
49
- response_entries.flatten.each do |res|
56
+ # Process the response depending on the requests lookup_type
57
+ # if it is a :first lookup then we only want to set the result
58
+ # once, if it's cascading, we should ammend the payload array
59
+ #
60
+ def process_response(res)
50
61
  case request.lookup_type
51
62
  when :first
52
- @answer.payload ||= res[:value]
53
- @answer.datatype ||= res[:datatype]
63
+ @answer.payload ||= res[:value]
64
+ @answer.datatype ||= res[:datatype]
54
65
  when :cascade
55
- @answer.payload << res[:value]
66
+ @answer.payload << res[:value]
56
67
  end
57
68
  end
58
69
 
59
- if request.lookup_type == :cascade && @answer.payload.is_a?(Array)
60
- case request.merge
61
- when :array
62
- @answer.flatten_payload!
63
- when :hash,:deep_hash
64
- @answer.merge_payload!(request.merge)
70
+ # Once all the responses are submitted into the answers payload
71
+ # we need to consolidate the data based on the merge behaviour
72
+ # requested.
73
+ #
74
+ def consolidate_answer
75
+ if request.lookup_type == :cascade && @answer.payload.is_a?(Array)
76
+ case request.merge
77
+ when :array
78
+ @answer.flatten_payload!
79
+ when :hash, :deep_hash
80
+ @answer.merge_payload!(request.merge)
81
+ end
65
82
  end
66
83
  end
67
-
68
84
  end
69
85
  end
70
-
@@ -1,6 +1,7 @@
1
+ require 'jerakia'
2
+ require 'jerakia/log'
1
3
  class Jerakia
2
4
  class Request
3
-
4
5
  attr_accessor :key
5
6
  attr_accessor :namespace
6
7
  attr_accessor :merge
@@ -11,7 +12,7 @@ class Jerakia
11
12
  attr_accessor :scope_options
12
13
  attr_accessor :use_schema
13
14
 
14
- def initialize(opts={})
15
+ def initialize(opts = {})
15
16
  options = defaults.merge(opts)
16
17
  @key = options[:key]
17
18
  @namespace = options[:namespace]
@@ -17,30 +17,27 @@ require 'yaml'
17
17
  class Jerakia::Response
18
18
  module Filter
19
19
  module Encryption
20
-
21
- def filter_encryption(opts={})
20
+ def filter_encryption(_opts = {})
22
21
  parse_values do |val|
23
- if val.is_a?(String)
24
- decrypt val
25
- end
22
+ decrypt val if val.is_a?(String)
26
23
  val
27
24
  end
28
25
  end
29
26
 
30
27
  def decrypt(data)
31
28
  if encrypted?(data)
32
- public_key = config["eyaml"]["public_key"]
33
- private_key = config["eyaml"]["private_key"]
29
+ public_key = config['eyaml']['public_key']
30
+ private_key = config['eyaml']['private_key']
34
31
  Hiera::Backend::Eyaml::Options[:pkcs7_private_key] = private_key
35
32
  Hiera::Backend::Eyaml::Options[:pkcs7_public_key] = public_key
36
33
  parser = Hiera::Backend::Eyaml::Parser::ParserFactory.hiera_backend_parser
37
-
34
+
38
35
  tokens = parser.parse(data)
39
- decrypted = tokens.map{ |token| token.to_plain_text }
36
+ decrypted = tokens.map(&:to_plain_text)
40
37
  plaintext = decrypted.join
41
38
  Jerakia.log.debug(plaintext)
42
39
  plaintext.chomp!
43
- data.clear.insert(0,plaintext)
40
+ data.clear.insert(0, plaintext)
44
41
  else
45
42
  data
46
43
  end
@@ -52,5 +49,3 @@ class Jerakia::Response
52
49
  end
53
50
  end
54
51
  end
55
-
56
-
@@ -1,6 +1,6 @@
1
1
  # strsub is in output filter that matches tags in data and replaces them
2
2
  # for values in the scope. It mimics the hiera features of being able to
3
- # embed %{::var} in YAML documents. This output filter may not provide
3
+ # embed %{::var} in YAML documents. This output filter may not provide
4
4
  # 100% compatibility to hiera but it should cover most scenarios.
5
5
  #
6
6
  # Jerakia does not support method or literal interpolations, just straightforward %{var} and %{::var}
@@ -10,12 +10,9 @@
10
10
  class Jerakia::Response
11
11
  module Filter
12
12
  module Strsub
13
-
14
- def filter_strsub(opts={})
13
+ def filter_strsub(_opts = {})
15
14
  parse_values do |val|
16
- if val.is_a?(String)
17
- do_substr(val)
18
- end
15
+ do_substr(val) if val.is_a?(String)
19
16
  val
20
17
  end
21
18
  end
@@ -24,12 +21,10 @@ class Jerakia::Response
24
21
  data.gsub!(/%\{([^\}]*)\}/) do |tag|
25
22
  Jerakia.log.debug("matched substr #{tag}")
26
23
  scopekey = tag.match(/\{([^\}]+)\}/)[1]
27
- scopekey.gsub!(/^::/,'')
24
+ scopekey.gsub!(/^::/, '')
28
25
  lookup.scope[scopekey.to_sym]
29
26
  end
30
27
  end
31
28
  end
32
29
  end
33
30
  end
34
-
35
-
@@ -1,9 +1,9 @@
1
1
  class Jerakia::Response
2
2
  module Filter
3
- def filter!(name,opts)
4
- Jerakia::Util.autoload('response/filter', name)
5
- instance_eval "extend Jerakia::Response::Filter::#{name.to_s.capitalize}"
6
- instance_eval "self.filter_#{name.to_s} (#{opts})"
7
- end
3
+ def filter!(name, opts)
4
+ Jerakia::Util.autoload('response/filter', name)
5
+ instance_eval "extend Jerakia::Response::Filter::#{name.to_s.capitalize}"
6
+ instance_eval "self.filter_#{name} (#{opts})"
7
+ end
8
8
  end
9
9
  end
@@ -1,17 +1,16 @@
1
1
  class Jerakia::Response < Jerakia
2
-
3
2
  attr_accessor :entries
4
3
  attr_reader :lookup
5
4
 
6
5
  def initialize(lookup)
7
- @entries=[]
8
- @lookup=lookup
6
+ @entries = []
7
+ @lookup = lookup
9
8
  require 'jerakia/response/filter'
10
9
  extend Jerakia::Response::Filter
11
10
  end
12
11
 
13
12
  def want?
14
- if lookup.request.lookup_type == :first && entries.length > 0
13
+ if lookup.request.lookup_type == :first && !entries.empty?
15
14
  return false
16
15
  else
17
16
  return true
@@ -20,14 +19,14 @@ class Jerakia::Response < Jerakia
20
19
 
21
20
  def submit(val)
22
21
  Jerakia.log.debug "Backend submitted #{val}"
23
- unless want?
24
- no_more_answers
25
- else
22
+ if want?
26
23
  @entries << {
27
24
  :value => val,
28
25
  :datatype => val.class.to_s.downcase
29
26
  }
30
27
  Jerakia.log.debug "Added answer #{val}"
28
+ else
29
+ no_more_answers
31
30
  end
32
31
  end
33
32
 
@@ -44,14 +43,9 @@ class Jerakia::Response < Jerakia
44
43
  end
45
44
  entry
46
45
  end
47
-
48
46
  end
49
-
50
-
51
47
 
52
48
  def no_more_answers
53
- Jerakia.log.debug "warning: backend tried to submit too many answers"
49
+ Jerakia.log.debug 'warning: backend tried to submit too many answers'
54
50
  end
55
-
56
51
  end
57
-