<p>Okay, so it turns out this wasn't my problem, but I know the answer to the question I asked:</p>

<p>The file <code>/opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb</code> defines a function <code>get_setting</code>, which looks like this:</p>

<pre><code>def get_setting(name)
  settings[config[:json_config]][name]
end
</code></pre>

<p><code>config[:json_config]</code> is the string passed with the <code>-j</code> switch.</p>

<p>On to <code>config</code>: the file <code>/opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugin-2.7.1/lib/sensu-handler.rb</code> requires <code>sensu-plugin/utils</code>, which defines the following:</p>

<pre><code>module Sensu
  module Plugin
    module Utils # rubocop:disable Metrics/ModuleLength
      def config_files
        if ENV['SENSU_LOADED_TEMPFILE'] &amp;&amp; File.file?(ENV['SENSU_LOADED_TEMPFILE'])
          IO.read(ENV['SENSU_LOADED_TEMPFILE']).split(':')
        elsif ENV['SENSU_CONFIG_FILES']
          ENV['SENSU_CONFIG_FILES'].split(':')
        else
          ['/etc/sensu/config.json'] + Dir['/etc/sensu/conf.d/**/*.json']
        end
      end

      def load_config(filename)
        JSON.parse(File.open(filename, 'r').read)
      rescue
        {}
      end

      def settings
        @settings ||= config_files.map { |f| load_config(f) }.reduce { |a, b| deep_merge(a, b) }
      end
...
</code></pre>

<p>So <code>settings</code> is going to load all JSON files nested anywhere under <code>/etc/sensu/conf.d/</code> and deep-merge them. Beyond that, the path is not relevant to Sensu's ability to load it using the string <code>slack</code>; that's the name of the entry in the JSON payload. This mechanism is common to all Sensu config, not just handler plugins. Documentation is here: <a href="https://docs.sensu.io/sensu-core/1.8/reference/configuration/" rel="nofollow noreferrer">https://docs.sensu.io/sensu-core/1.8/reference/configuration/</a>.</p>

<p>I looked at my log files, and it does seem to be finding and loading the files I had optimistically placed under <code>/etc/sensu/conf.d/teams.d/</code>, but it's not working.</p>

<p>Before I found the answer documented here, I took a stab and renamed the directory from <code>teams.d</code> to <code>microsoft-teams.d</code>, and the configuration object defined within from <code>teams</code> to <code>microsoft-teams</code>. I don't know why that would have had any effect, but it's working now. Maybe I had fat-fingered or copypasta'd something and resolved that inadvertently in the process of adding the <code>microsoft</code> prefix.</p>

CC BY-SA 4.0
2019-09-25T18:32:26.680Z
2019-09-25T18:32:26.680Z
JakeRobb
<p>So, we're transitioning from Slack to MSTeams. I'm tasked with updating our Sensu configuration so that alerts get sent to MSTeams channels instead of Slack channels.</p>

<p>In the <code>/etc/sensu/conf.d/handlers.json</code>, we have Slack notifications configured as follows:</p>

<pre><code>...
"slack" : {
    "type": "pipe",
    "command": "handler-slack.rb -j slack",
    "filters": [
        "occurrences"
    ]
},
....
</code></pre>

<p>That <code>-j slack</code> tells the handler to load a JSON config file which is located at <code>/etc/sensu/conf.d/slack.d/slack.json</code>.</p>

<p>The Teams handler is configured similarly:</p>

<pre><code>...
"microsoft-teams" : {
    "type": "pipe",
    "command": "handler-microsoft-teams.rb -j microsoft-teams",
    "filters": [
        "occurrences"
    ]
},
...
</code></pre>

<p>But when Sensu tries to pass a notification to the Teams handler, I get an error that leads me to believe that the JSON config wasn't loaded:</p>

<pre><code>/opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb:104:in `get_setting': undefined method `[]' for nil:NilClass (NoMethodError)
    from /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb:28:in `payload_template'
    from /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb:108:in `handle'
    from /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugin-2.7.1/lib/sensu-handler.rb:89:in `block in &lt;class:Handler&gt;'
</code></pre>

<p><code>payload_template</code> is the first setting that handler-microsoft-teams.rb attempts to load. It is not specified in my JSON, and the code is written to treat it as optional. </p>

<p>So, my question is this: how does the Slack plugin go from the string passed to the <code>-j</code> argument (<code>slack</code>) to the path <code>/etc/sensu/conf.d/slack.d/slack.json</code>? And how do I determine the equivalent path magic for the Teams plugin? I don't know if this magic is inherent in Ruby (in which I'm a non-expert), Sensu, sensu-plugins, or something else. I've been digging in code for a while now and I'm coming up empty.</p>

<p>(Yes, I'm aware of Sensu Go and would like to upgrade; that's not an option on my current timeline.)</p>

<sensu>
How do Sensu handler notification plugins determine the path of their JSON config files?
JakeRobb
# How do Sensu handler notification plugins determine the path of their JSON config files?

(Asked by: username_0 on 2019-09-25)

So, we're transitioning from Slack to MSTeams. I'm tasked with updating our Sensu configuration so that alerts get sent to MSTeams channels instead of Slack channels.
In the /etc/sensu/conf.d/handlers.json, we have Slack notifications configured as follows:
...
"slack" : {
    "type": "pipe",
    "command": "handler-slack.rb -j slack",
    "filters": [
        "occurrences"
    ]
},
....

That -j slack tells the handler to load a JSON config file which is located at /etc/sensu/conf.d/slack.d/slack.json.
The Teams handler is configured similarly:
...
"microsoft-teams" : {
    "type": "pipe",
    "command": "handler-microsoft-teams.rb -j microsoft-teams",
    "filters": [
        "occurrences"
    ]
},
...

But when Sensu tries to pass a notification to the Teams handler, I get an error that leads me to believe that the JSON config wasn't loaded:
/opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb:104:in `get_setting': undefined method `[]' for nil:NilClass (NoMethodError)
    from /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb:28:in `payload_template'
    from /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb:108:in `handle'
    from /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugin-2.7.1/lib/sensu-handler.rb:89:in `block in '

payload_template is the first setting that handler-microsoft-teams.rb attempts to load. It is not specified in my JSON, and the code is written to treat it as optional. 
So, my question is this: how does the Slack plugin go from the string passed to the -j argument (slack) to the path /etc/sensu/conf.d/slack.d/slack.json? And how do I determine the equivalent path magic for the Teams plugin? I don't know if this magic is inherent in Ruby (in which I'm a non-expert), Sensu, sensu-plugins, or something else. I've been digging in code for a while now and I'm coming up empty.
(Yes, I'm aware of Sensu Go and would like to upgrade; that's not an option on my current timeline.)

**Tags:** sensu


---

## 1 answers

### Answer by username_0 on 2019-09-25. Score: 2 | Accepted answer

Okay, so it turns out this wasn't my problem, but I know the answer to the question I asked:
The file /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugins-microsoft-teams-2.0.0/bin/handler-microsoft-teams.rb defines a function get_setting, which looks like this:
def get_setting(name)
  settings[config[:json_config]][name]
end

config[:json_config] is the string passed with the -j switch.
On to config: the file /opt/sensu/embedded/lib/ruby/gems/2.4.0/gems/sensu-plugin-2.7.1/lib/sensu-handler.rb requires sensu-plugin/utils, which defines the following:
module Sensu
  module Plugin
    module Utils # rubocop:disable Metrics/ModuleLength
      def config_files
        if ENV['SENSU_LOADED_TEMPFILE'] && File.file?(ENV['SENSU_LOADED_TEMPFILE'])
          IO.read(ENV['SENSU_LOADED_TEMPFILE']).split(':')
        elsif ENV['SENSU_CONFIG_FILES']
          ENV['SENSU_CONFIG_FILES'].split(':')
        else
          ['/etc/sensu/config.json'] + Dir['/etc/sensu/conf.d/**/*.json']
        end
      end

      def load_config(filename)
        JSON.parse(File.open(filename, 'r').read)
      rescue
        {}
      end

      def settings
        @settings ||= config_files.map { |f| load_config(f) }.reduce { |a, b| deep_merge(a, b) }
      end
...

So settings is going to load all JSON files nested anywhere under /etc/sensu/conf.d/ and deep-merge them. Beyond that, the path is not relevant to Sensu's ability to load it using the string slack; that's the name of the entry in the JSON payload. This mechanism is common to all Sensu config, not just handler plugins. Documentation is here: https://docs.sensu.io/sensu-core/1.8/reference/configuration/.
I looked at my log files, and it does seem to be finding and loading the files I had optimistically placed under /etc/sensu/conf.d/teams.d/, but it's not working.
Before I found the answer documented here, I took a stab and renamed the directory from teams.d to microsoft-teams.d, and the configuration object defined within from teams to microsoft-teams. I don't know why that would have had any effect, but it's working now. Maybe I had fat-fingered or copypasta'd something and resolved that inadvertently in the process of adding the microsoft prefix.


---

