<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
  <title>Mike Lanyon's Blog</title>
  <description>Notes and thoughts from LanyonM</description>
  <link>https://blog.lanyonm.org</link>
  <atom:link href="https://blog.lanyonm.org/feed.xml" rel="self" type="application/rss+xml" />
  
  <item>
    <title>Creative &amp; Technology: A Partnership - DevOpsDays MSN 2016</title>
    <description>&lt;div class=&quot;center quote&quot;&gt;
  &quot;The premise that dev and ops are at odds is a fallacy.&quot;
&lt;/div&gt;

&lt;p&gt;This talk has been brewing for over two years. While talking to &lt;a href=&quot;https://www.twitter.com/patrickdebois&quot;&gt;Patrick Debois&lt;/a&gt; at DevOpsDays Minneapolis 2014 the conversation meandered outside the typical confines of dev &amp;amp; ops. It was around the time Patrick joined Small Town Heroes, and I was reflecting on the parallels between themes at DevOpsDays and my work at Critical Mass. It felt like the issues we as a community were discussing were much more easily reconciled than the approach differences of Creative and Technology in my work environment.&lt;/p&gt;

&lt;p&gt;I have come to believe that &lt;em&gt;the premise that dev and ops are at odds is a fallacy&lt;/em&gt;. Sure, there are perverse organizational structures that silo expertise and incent them against one another, but I’m operating under the assumption that such easily identified wrongs aren’t present. What I was left to ponder (and later enumerate) were strategies and tactics that can be used to create high-performance, cohesive, multi-disciplinary teams.&lt;/p&gt;

&lt;p&gt;Here’s an abridged outline of what I covered in the talk:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Mission statement&lt;/li&gt;
  &lt;li&gt;Participating &amp;amp; sharing in each others’ work process&lt;/li&gt;
  &lt;li&gt;Building trust&lt;/li&gt;
  &lt;li&gt;Surprise reduction&lt;/li&gt;
  &lt;li&gt;Psychological safety&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;center quote&quot;&gt;
  &quot;Understand your assumptions and&lt;br /&gt;anticipate other&apos;s needs.&quot;
&lt;/div&gt;

&lt;p&gt;DevOps is about tools and culture. As much as I enjoy the occasional tool-smithing indulgence, I find the topic of leadership’s role in creating an environment that nurtures high-performance teams even more enjoyable and satisfying. Leave a comment or ping me on Twitter if you’d like to discuss further. Cheers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://speakerdeck.com/lanyonm/creative-and-technology-a-partnership-devopsdays-msn-2016&quot;&gt;https://speakerdeck.com/lanyonm/creative-and-technology-a-partnership-devopsdays-msn-2016&lt;/a&gt;&lt;/p&gt;

&lt;script async=&quot;&quot; class=&quot;speakerdeck-embed&quot; data-id=&quot;1aaef89e52d0487489a2adb6e0020917&quot; data-ratio=&quot;1.77777777777778&quot; src=&quot;https://speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;

</description>
    <pubDate>Wed, 02 Nov 2016 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/speaking/2016/11/02/creative-and-technology-a-partnership-devopsdays-msn.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/speaking/2016/11/02/creative-and-technology-a-partnership-devopsdays-msn.html</guid>
  </item>
  
  <item>
    <title>Log Aggregation with Log4j, Spring, and Logstash</title>
    <description>&lt;p&gt;While parsing raw log files is a fine way for Logstash to ingest data, there are several other methods to ship the same information to Logstash. These methods each have trade-offs that may make them more or less suitable for your particular situation. I have posted about &lt;a href=&quot;/articles/2014/01/12/logstash-multiline-tomcat-log-parsing.html&quot;&gt;multiline tomcat log parsing&lt;/a&gt; before, and this post is an attempt to compare that and other methods I’ve explored: log4j as JSON, log4j over TCP, and raw log4j with the multiline codec.&lt;/p&gt;

&lt;p&gt;These examples were developed on one machine but are designed to work in an environment where your ELK stack is on a separate machine/instance/container. In the multi-machine environment &lt;a href=&quot;https://www.elastic.co/products/beats/filebeat&quot;&gt;Filebeat&lt;/a&gt; (formerly logstash-forwarder) would be used in cases where the example uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; input.&lt;/p&gt;

&lt;p&gt;For posterity’s sake, these are the software versions used in this example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Java 7u67&lt;/li&gt;
  &lt;li&gt;Spring 4.2.3&lt;/li&gt;
  &lt;li&gt;Logstash 2.1.0&lt;/li&gt;
  &lt;li&gt;Elasticsearch 2.1.1&lt;/li&gt;
  &lt;li&gt;Kibana 4.3.1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I first began to author this post on Nov, 28th 2014, so please forgive any options presented that are no longer in favor. Also, you’ll notice that slf4j is used as an abstraction for log4j in the code samples.&lt;/p&gt;

&lt;h2 id=&quot;log4j-as-json&quot;&gt;Log4j As JSON&lt;/h2&gt;
&lt;p&gt;This method aims to have log4j log as JSON and then use Logstash’s &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/2.1/plugins-inputs-file.html&quot;&gt;file input&lt;/a&gt; with a &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/2.1/plugins-codecs-json.html&quot;&gt;json codec&lt;/a&gt; to ingest the data. This will avoid unnecessary grok parsing and the thread &lt;em&gt;unsafe&lt;/em&gt; &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/2.1/plugins-filters-multiline.html&quot;&gt;multiline filter&lt;/a&gt;. Seeing json-formatted logs can be jarring for a Java dev (no pun intended), but reading individual log files should be a thing of the past once you’re up and running with log aggregation. Also, you can run two appenders in parallel if you have the available disk space.&lt;/p&gt;

&lt;p&gt;Instead of using a PatternLayout with a heinously complex ConversionPattern, let’s have a look at &lt;a href=&quot;https://github.com/logstash/log4j-jsonevent-layout&quot;&gt;log4j-jsonevent-layout&lt;/a&gt;. The prospect of a solution that is entirely in configuration fits the bill, and I can live with yet another logging dependency.&lt;/p&gt;

&lt;p&gt;Including the dependency in a &lt;a href=&quot;https://github.com/lanyonm/playground/blob/2feedd53f5628ba515dca86f89ac7d4660713e3f/pom.xml#L73-L79&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;&lt;/a&gt; is quite easy:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.logstash.log4j&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jsonevent-layout&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.7&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/lanyonm/playground/blob/2feedd53f5628ba515dca86f89ac7d4660713e3f/src/main/resources/log4j.properties#L22-L26&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4j.properties&lt;/code&gt;&lt;/a&gt; will look familiar, and I’ll explain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserFields&lt;/code&gt; in a bit.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-properties&quot; data-lang=&quot;properties&quot;&gt;&lt;span class=&quot;py&quot;&gt;log4j.rootLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debug,json&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;log4j.appender.json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;org.apache.log4j.DailyRollingFileAppender&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.json.File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;target/app.log&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.json.DatePattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.yyyy-MM-dd&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.json.layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;net.logstash.log4j.JSONEventLayoutV1&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.json.layout.UserFields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;application:playground,environment:dev&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the Logstash config is as you’d expect:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;codec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;log4j-json&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/path/to/target/app.log&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The values set in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserFields&lt;/code&gt; are important because they allow the additional log metadata (taxonomy) to be set in the application configuration. This is information about the application and environment that will allow the log aggregation system to categorize the data. Because we’re using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; input plugin we could also use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_field&lt;/code&gt;, but this would require separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; plugins statements for every application. Certainly possible, but even with configuration management more of a headache than the alternative. Also, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path&lt;/code&gt; parameter of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; plugin is an array so we can specify multiple files with ease.&lt;/p&gt;

&lt;p&gt;If everything is set correctly, log messages should look like this in Kibana:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/log4j-json-logstash-kibana.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/log4j-json-logstash-kibana-min.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;A log message from Playground using log4j-jsonevent-layout&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;As you can see, the UserFields are parsed into Logstash fields. If you prefer these values to be set via command line and environment variable, the library &lt;a href=&quot;https://github.com/logstash/log4j-jsonevent-layout#command-line&quot;&gt;provides a way&lt;/a&gt; that will &lt;em&gt;override&lt;/em&gt; anything set in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4j.properties&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;log4j-over-tcp&quot;&gt;Log4j over TCP&lt;/h2&gt;
&lt;p&gt;This method uses log4j’s &lt;a href=&quot;https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/SocketAppender.html&quot;&gt;SocketAppender&lt;/a&gt; and Logstash’s &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/2.1/plugins-inputs-log4j.html&quot;&gt;log4j input&lt;/a&gt;. Log events are converted into a binary format via the SocketAppender and streamed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4j&lt;/code&gt; input. The advantages here are that the new log4j appender can be added without additional dependencies and that we are able to avoid dealing with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multiline&lt;/code&gt; filter. Let’s look at the implementation before digging into the shortcomings.&lt;/p&gt;

&lt;p&gt;Here’s a snippet of the &lt;a href=&quot;https://github.com/lanyonm/playground/blob/2feedd53f5628ba515dca86f89ac7d4660713e3f/src/main/resources/log4j.properties#L16-L20&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4j.properties&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-properties&quot; data-lang=&quot;properties&quot;&gt;&lt;span class=&quot;py&quot;&gt;log4j.rootLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debug,tcp&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;log4j.appender.tcp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;org.apache.log4j.net.SocketAppender&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.tcp.Port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3456&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.tcp.RemoteHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.tcp.ReconnectionDelay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10000&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.tcp.Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;playground&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the corresponding snippet of Logstash config:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;log4j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;server&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3456&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;log4j&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;One of the log4j configurations above that you rarely see is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Application&lt;/code&gt;. When this parameter is set Logstash will parse it into an event field. This is handy, but may not satisfy your logging taxonomy - exposing one of this method’s shortcomings: &lt;em&gt;tagging log events with application and environment identifying information&lt;/em&gt;. The way this is typically done in the Logstash config is with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_field&lt;/code&gt; on an input plugin. Taking the typical approach would mean a different input plugin/port for each java app sending logs - not fun to manage at scale!&lt;/p&gt;

&lt;h3 id=&quot;mapped-diagnostic-context&quot;&gt;Mapped Diagnostic Context&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html&quot;&gt;Mapped Diagnostic Context&lt;/a&gt; (MDC) provides a way to enrich standard log information via a map of values of interest. Thankfully the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4j&lt;/code&gt; plugin will parse MDC hashes into log event fields. The MDC is managed on a per-thread basis, but a child thread automatically inherits a copy of the MDC from it’s parent. This means that log taxonomy can be set to the MDC in the application’s main thread and affect every log statement for its entire lifespan.&lt;/p&gt;

&lt;p&gt;Here’s minimalistic example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.slf4j.Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.slf4j.LoggerFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.slf4j.MDC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoggingTaxonomy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoggerFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;LoggingTaxonomy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;MDC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;environment&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;APP_ENV&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// run the app&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;the app is running!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With a PatternLayout conversion pattern like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%d{ABSOLUTE} %5p %c{1}:%L - %X{environment} - %m%n&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APP_ENV&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt; you’d expect to see a log statement like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;15:23:03,698 DEBUG LoggingTaxonomy:34 - dev - the app is running!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;How and where to integrate the MDC values will vary widely based on the framework used by the application, but every framework I’ve ever used has an appropriate place to set this information. In Spring MVC with Java Config it can go in one of the &lt;a href=&quot;https://github.com/lanyonm/playground/blob/2feedd53f5628ba515dca86f89ac7d4660713e3f/src/main/java/org/lanyonm/playground/config/AppInitializer.java#L23&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppInitializer&lt;/code&gt;&lt;/a&gt; methods. There are additional uses for MDC, which I’ll write about in a future post.&lt;/p&gt;

&lt;p&gt;Once all the code and config is correct, the enhanced logs will flow into your Kibana dashboard like so:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/log4j-tcp-logstash-kibana.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/log4j-tcp-logstash-kibana-min.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;A log message from Playground using Log4j over TCP and MDC for additional log event fields&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;A few things to note about this approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The logging level is stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;priority&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level&lt;/code&gt; as is with log4j-jsonevent-layout&lt;/li&gt;
  &lt;li&gt;There is no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source_host&lt;/code&gt; field, so you may need to add that via MDC as well&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;raw-log4j-and-the-multiline-codec&quot;&gt;Raw Log4j and the Multiline Codec&lt;/h2&gt;
&lt;p&gt;My &lt;a href=&quot;/articles/2014/01/12/logstash-multiline-tomcat-log-parsing.html&quot;&gt;multiline parsing post&lt;/a&gt; used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multiline&lt;/code&gt; filter plugin, but as mentioned above that plugin isn’t threadsafe. I wanted to provide a slight update to that approach that uses the &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/2.1/plugins-codecs-multiline.html&quot;&gt;multiline codec&lt;/a&gt; instead of the filter. I’ve modified the original example as little as possible and integrated the relevant bits into the &lt;a href=&quot;https://github.com/lanyonm/playground/tree/2feedd53f5628ba515dca86f89ac7d4660713e3f&quot;&gt;playground&lt;/a&gt; app.&lt;/p&gt;

&lt;p&gt;Here’s a snippet of the &lt;a href=&quot;https://github.com/lanyonm/playground/blob/2feedd53f5628ba515dca86f89ac7d4660713e3f/src/main/resources/log4j.properties#L10-L14&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4j.properties&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-properties&quot; data-lang=&quot;properties&quot;&gt;&lt;span class=&quot;py&quot;&gt;log4j.rootLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debug,file&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;log4j.appender.file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;org.apache.log4j.DailyRollingFileAppender&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.file.File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;target/file.log&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.file.layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;org.apache.log4j.PatternLayout&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;log4j.appender.file.layout.ConversionPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%d{yyyy-MM-dd HH:mm:ss,SSS ZZZ} | %p | %c - %m%n&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the corresponding snippet of Logstash config (the grok pattern file can be found &lt;a href=&quot;https://gist.github.com/lanyonm/8390458#file-grok-patterns&quot;&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;raw-file&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/path/to/target/file.log&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;add_field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;application&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;playground&quot;&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;environment&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dev&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;codec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multiline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;patterns_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/path/to/logstash/patterns&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;(^%{TOMCAT_DATESTAMP_PATTERN})|(^%{CATALINA_DATESTAMP_PATTERN})&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;negate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;what&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;previous&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You’ll notice that I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_field&lt;/code&gt; to add &lt;em&gt;application&lt;/em&gt; and &lt;em&gt;environment&lt;/em&gt; fields because adding those to the ConversionPattern and grok parsers would’ve required some heavy lifting. If I’d built-out this solution fully, I would have integrated these via MDC as described above and made the ConversionPattern and Grok parse updates.&lt;/p&gt;

&lt;p&gt;The log message will look like this in Kibana:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/log4j-raw-logstash-kibana.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/log4j-raw-logstash-kibana-min.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;A log message from Playground using the file input and multiline codec to parse a raw Log4j&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;In my opinion there are several shortcomings to this approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Creating multiline parsers can be tough.&lt;/li&gt;
  &lt;li&gt;Grok parse patterns are tightly coupled to Conversion pattern and require adjustments in both places for changes.&lt;/li&gt;
  &lt;li&gt;Developers won’t be able to add MDC information and have it automagically show up in the log aggregation system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;other-options&quot;&gt;Other Options&lt;/h2&gt;
&lt;p&gt;Log4j isn’t the only logging solution for Java. &lt;a href=&quot;http://logback.qos.ch/&quot;&gt;Logback&lt;/a&gt; is growing in popularity and implements the slf4j API making it swappable with Log4j or JUL. The &lt;a href=&quot;https://github.com/logstash/logstash-logback-encoder&quot;&gt;logstash-logback-encoder&lt;/a&gt; looks particularly robust. If you’re coming from a Log4j implementation be sure to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LogstashTcpSocketAppender&lt;/code&gt;, not the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LogstashSocketAppender&lt;/code&gt;. The latter uses UDP and debugging an incident where log messages may have been dropped is a recipe for disaster. Given more time this would be my next exploration.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I hope the comparison of these methods is helpful. If it’s not already clear, my preference is log4j as JSON using &lt;a href=&quot;https://github.com/logstash/log4j-jsonevent-layout&quot;&gt;log4j-jsonevent-layout&lt;/a&gt; and Filebeat. Logstash-forwarder or Filebeat is already on many of our servers, so this is an easy approach for us. I’m interested to hear others’ experiences managing their log aggregation pipeline.&lt;/p&gt;
</description>
    <pubDate>Tue, 29 Dec 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/12/29/log-aggregation-log4j-spring-logstash.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/12/29/log-aggregation-log4j-spring-logstash.html</guid>
  </item>
  
  <item>
    <title>Continuous Security with OWASP&apos;s Dependency Check Maven Plugin</title>
    <description>&lt;p&gt;On the heels of the &lt;a href=&quot;/articles/2015/12/19/publish-maven-site-github-pages-travis-ci.html&quot;&gt;previous post&lt;/a&gt; about continuously delivering documentation I wanted to show how easy it is to integrate dependency vulnerability checks and reports into a Maven-based delivery pipeline. The &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project&quot;&gt;OWASP Top 10 2013&lt;/a&gt; contains an entry about &lt;a href=&quot;https://www.owasp.org/index.php/Top_10_2013-A9-Using_Components_with_Known_Vulnerabilities&quot;&gt;Using Components with Known Vulnerabilities&lt;/a&gt;, so being able to check project dependencies against a canonical list of vulnerable libraries is critical to compliance with the Top 10. The &lt;a href=&quot;https://www.owasp.org/index.php/OWASP_Dependency_Check&quot;&gt;OWASP Dependency Check&lt;/a&gt; utility uses NIST’s &lt;a href=&quot;https://nvd.nist.gov/&quot;&gt;National Vulnerability Database&lt;/a&gt; (NVD) to identify the vulnerable dependencies, so the list is always up-to-date.&lt;/p&gt;

&lt;p&gt;The Dependency Check utility is conveniently wrapped by the &lt;a href=&quot;https://jeremylong.github.io/DependencyCheck/dependency-check-maven/&quot;&gt;dependency-check-maven&lt;/a&gt; plugin. The usage information contains several examples and the configuration allows plenty of tuning for the analyzer. With a single configuration parameter the plugin can fail a build if a vulnerable dependency is found.&lt;/p&gt;

&lt;h2 id=&quot;cvss-scores-and-the-pom&quot;&gt;CVSS Scores and the POM&lt;/h2&gt;
&lt;p&gt;The Common Vulnerability Scoring System (CVSS) uses several aspects of exploit-ability and impact to determine a &lt;em&gt;base score&lt;/em&gt;. Each vulnerability in the NVD has a base score attached, and anything above 4.0 is considered Medium or High severity (&lt;a href=&quot;https://nvd.nist.gov/cvss.cfm&quot;&gt;CVSS overview&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Events following the publishing of a CVE can affect the CVSS score (e.g., an exploit kit is published). These adjusted scores are referred to as &lt;em&gt;temporal scores&lt;/em&gt;, and CVSS provides &lt;a href=&quot;https://nvd.nist.gov/cvss.cfm?calculator&amp;amp;version=2&quot;&gt;a calculator&lt;/a&gt; to help understand the effect. The calculator is also quite handy to help understand what influences a score.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://jeremylong.github.io/DependencyCheck/dependency-check-maven/index.html&quot;&gt;the examples&lt;/a&gt;, configuring the POM is quite easy. Here’s the configuration I’m using in my &lt;a href=&quot;https://github.com/lanyonm/playground/blob/7bebfc970608c102b3d6be5951b24f9ba95459a9/pom.xml#L201&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.owasp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;dependency-check-maven&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.3.3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;cveValidForHours&amp;gt;&lt;/span&gt;12&lt;span class=&quot;nt&quot;&gt;&amp;lt;/cveValidForHours&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;failBuildOnCVSS&amp;gt;&lt;/span&gt;4&lt;span class=&quot;nt&quot;&gt;&amp;lt;/failBuildOnCVSS&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;check&lt;span class=&quot;nt&quot;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The configuration above will fail the Maven run if a dependency with a CVSS score above 4 is found and will recheck the NVD only every 12 hours. To run a standalone dependency check:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mvn dependency-check:check
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;report-integration&quot;&gt;Report Integration&lt;/h2&gt;
&lt;p&gt;Integrating a dependency check report into the generated Maven site is also quite easy thanks to the examples. Here’s the relevant snippet of the &lt;a href=&quot;https://github.com/lanyonm/playground/blob/7bebfc970608c102b3d6be5951b24f9ba95459a9/pom.xml#L357&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;reportPlugins&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.owasp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;dependency-check-maven&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.3.3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Dependency Check&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;reportSets&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;reportSet&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;reports&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;report&amp;gt;&lt;/span&gt;aggregate&lt;span class=&quot;nt&quot;&gt;&amp;lt;/report&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/reports&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/reportSet&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/reportSets&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/reportPlugins&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A “Dependency Check” link will be added to the Project Reports page of the generated site that points to a page that looks like this:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/dependency-check-report.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/dependency-check-report.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;OWASP&apos;s Dependency Check showing CVE-2015-6420 in Apache&apos;s commons-collections:3.2.1&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;For illustrative purposes I included a known vulnerable dependency. Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn site&lt;/code&gt; didn’t cause the build to fail because the plugin configuration used during the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site&lt;/code&gt; lifecycle phase is separate from what was defined for the standalone check.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;While not a &lt;em&gt;full&lt;/em&gt; solution for security in your continuous delivery process, the dependency-check-maven plugin is a great way for a Java project to check the A9 box on the OWASP Top 10. Additionally, the visibility that the report gives dependency security is fantastic.&lt;/p&gt;

&lt;p&gt;One handy feature I didn’t mention is that a &lt;a href=&quot;https://jeremylong.github.io/DependencyCheck/general/suppression.html&quot;&gt;suppression list&lt;/a&gt; can be used to ignore positives. This can be used for false positives, or to ignore acknowledged vulnerabilities currently awaiting resolution in the backlog. This would allow the CI process to continue to run while the project is brought into compliance.&lt;/p&gt;
</description>
    <pubDate>Tue, 22 Dec 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/12/22/continuous-security-owasp-java-vulnerability-check.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/12/22/continuous-security-owasp-java-vulnerability-check.html</guid>
  </item>
  
  <item>
    <title>Publishing a Maven Site to GitHub Pages with Travis-CI</title>
    <description>&lt;p&gt;Part of continuous delivery is continuously delivering documentation along with the software. I’d go so far as to argue that it’s &lt;em&gt;part of&lt;/em&gt; the software. In the past I’ve had to remember to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn site&lt;/code&gt; periodically to ensure that the latest &lt;a href=&quot;http://blog.lanyonm.org/playground/apidocs/index.html&quot;&gt;javadoc&lt;/a&gt; and &lt;a href=&quot;http://blog.lanyonm.org/playground/dependency-updates-report.html&quot;&gt;dependency updates report&lt;/a&gt; get created, but &lt;em&gt;there’s gotta be a better way!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The key to this integration is having Travis authenticate back to GitHub to publish to gh-pages in a secure way. The default suggestion for publishing with GitHub’s &lt;a href=&quot;https://github.com/github/maven-plugins&quot;&gt;site-maven-plugin&lt;/a&gt; is to add your username &amp;amp; password or an oauth token to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.m2/settings.xml&lt;/code&gt;. This won’t work for Travis-CI and would leak the oauth token.&lt;/p&gt;

&lt;h2 id=&quot;pom-configuration&quot;&gt;POM Configuration&lt;/h2&gt;
&lt;p&gt;The site-maven-plugin configuration is exactly the same as &lt;a href=&quot;https://github.com/github/maven-plugins#example&quot;&gt;GitHub’s example&lt;/a&gt;, but that’s to be expected. The important configuration is to allow the oauth token to be read from an environment variable (excerpt from &lt;a href=&quot;https://github.com/lanyonm/playground/blob/85543d301e0955e3f6031053fe720888df58c53c/pom.xml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;project&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;github.global.server&amp;gt;&lt;/span&gt;github&lt;span class=&quot;nt&quot;&gt;&amp;lt;/github.global.server&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;github.global.oauth2Token&amp;gt;&lt;/span&gt;${env.GITHUB_OAUTH_TOKEN}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/github.global.oauth2Token&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To be able to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn site&lt;/code&gt; locally you’ll need to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export GITHUB_OAUTH_TOKEN=&quot;your-github-personal-access-token&quot;&lt;/code&gt; or add that line to your dotfiles. To create the token follow these &lt;a href=&quot;https://help.github.com/articles/creating-an-access-token-for-command-line-use/&quot;&gt;instructions&lt;/a&gt;. The token I created has repo and user:email access.&lt;/p&gt;

&lt;h2 id=&quot;travis-ci-configuration&quot;&gt;Travis-CI Configuration&lt;/h2&gt;

&lt;h3 id=&quot;encrypted-environment-variable&quot;&gt;Encrypted Environment Variable&lt;/h3&gt;
&lt;p&gt;Getting your GitHub token into the Travis-CI environment var is &lt;a href=&quot;https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables&quot;&gt;very well documented&lt;/a&gt;. Here’s a handy copy/paste of the command you’ll want to run to encrypt your environment variable:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;travis encrypt &lt;span class=&quot;nv&quot;&gt;GITHUB_OAUTH_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your-github-personal-access-token&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--add&lt;/span&gt; env.global&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The one thing not covered in the Travis-CI docs page is that you can have multiple encrypted environment variables like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secure&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bigEncryptedString/One&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secure&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bigEncryptedString/Two&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;build-timeout&quot;&gt;Build Timeout&lt;/h3&gt;
&lt;p&gt;Another thing to consider is that the site-maven-plugin can take quite a while to prepare and upload the site html to your gh-pages branch. By default this process does not generate log statements and Travis-CI may time-out. You can either &lt;a href=&quot;https://docs.travis-ci.com/user/build-timeouts#Build-times-out-because-no-output-was-received&quot;&gt;have Travis extend the timeout&lt;/a&gt; or enable debug logging for Maven: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn site -X&lt;/code&gt;. I chose the second option because I didn’t want some other issue to cause long build times. The &lt;a href=&quot;https://github.com/lanyonm/playground/blob/85543d301e0955e3f6031053fe720888df58c53c/.travis.yml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt;&lt;/a&gt; has the full details.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;You can see this working in my Spring &lt;a href=&quot;https://github.com/lanyonm/playground&quot;&gt;playground&lt;/a&gt; repository which publishes &lt;a href=&quot;http://blog.lanyonm.org/playground/&quot;&gt;here&lt;/a&gt;. Please let me know if you find this helpful or have suggestions for improvements.&lt;/p&gt;
</description>
    <pubDate>Sat, 19 Dec 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/12/19/publish-maven-site-github-pages-travis-ci.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/12/19/publish-maven-site-github-pages-travis-ci.html</guid>
  </item>
  
  <item>
    <title>ChatOps: Pingdom Alerts Pushed into HipChat with AWS Lambda and API Gateway</title>
    <description>&lt;p&gt;You probably searched “pingdom alerts in hipchat” or “pingdom hipchat integration” and were unhappy to find that there’s no direct method to integrate the two services. I was too - but it gave me the chance to use the AWS API Gateway and AWS Lambda to connect the two services. I assume you’re relatively familiar with the functionality that API Gateway and Lambda provide as well as getting-started experience with Node.js.&lt;/p&gt;

&lt;h2 id=&quot;pingdom-webhooks&quot;&gt;Pingdom Webhooks&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://help.pingdom.com/hc/en-us/articles/203611322-Setting-up-a-Webhook-and-an-Alerting-Endpoint&quot;&gt;documentation&lt;/a&gt; on the Pingdom site describes how to find the webhook payload structure, so I followed their advice and observed the up &amp;amp; down webhook events. I don’t use it this demo, but each of the webhooks a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Request-Id&lt;/code&gt; header was sent with a value like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6645779c-18a9-473d-808e-2b74450c7347&lt;/code&gt;. You may find this useful for tracing purposes.&lt;/p&gt;

&lt;h3 id=&quot;down&quot;&gt;Down&lt;/h3&gt;
&lt;p&gt;As you can see, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assign&lt;/code&gt; webhook is a GET request with the message payload as an URI encoded json string. It would be nice if this was a POST, but ¯\_(ツ)_/¯.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /webhook-endpoint?message=%7B%22check%22%3A%20%221834565%22%2C%20%22checkname%22%3A%20%22just%20a%20test%22%2C%20%22host%22%3A%20%22www.example.com%22%2C%20%22action%22%3A%20%22assign%22%2C%20%22incidentid%22%3A%208765%2C%20%22description%22%3A%20%22down%22%7D
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The decoded querystring looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1834565&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;checkname&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;just a test&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;www.example.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;incidentid&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8765&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;up&quot;&gt;Up&lt;/h3&gt;
&lt;p&gt;Aka &lt;em&gt;notify_of_close&lt;/em&gt; or &lt;em&gt;resolved&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /webhook-endpoint?message=%7B%22check%22%3A%20%221834565%22%2C%20%22checkname%22%3A%20%22just%20a%20test%22%2C%20%22host%22%3A%20%22www.example.com%22%2C%20%22action%22%3A%20%22notify_of_close%22%2C%20%22incidentid%22%3A%208765%2C%20%22description%22%3A%20%22up%22%7D
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1834565&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;checkname&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;just a test&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;www.example.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;notify_of_close&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;incidentid&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8765&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;testing-with-curl&quot;&gt;Testing with curl&lt;/h3&gt;
&lt;p&gt;For testing purposes you may want a quick curl statement to act as Pingdom (so you don’t have to deliberately take a monitored endpoint down):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -H &apos;X-Request-Id: 6645779c-18a9-473d-808e-2b74450c7347&apos; https://1x1x1x1x1x.execute-api.us-east-1.amazonaws.com/prod/pingdom-webhook?message=%7B%22check%22%3A%20%221834565%22%2C%20%22checkname%22%3A%20%22just%20a%20test%22%2C%20%22host%22%3A%20%22www.example.com%22%2C%20%22action%22%3A%20%22assign%22%2C%20%22incidentid%22%3A%208765%2C%20%22description%22%3A%20%22down%22%7D
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we understand Pingdom’s webhook a bit better, let’s have a look at the AWS parts.&lt;/p&gt;

&lt;h2 id=&quot;aws-lambda&quot;&gt;AWS Lambda&lt;/h2&gt;
&lt;p&gt;Before we configure the API Gateway, let’s have a look at the Lambda function. We do this first because you’ll need to select the Lambda when you create the API Gateway resource. I chose to use Node.js, but the code is straightforward and should be able to be ported to python easily.&lt;/p&gt;

&lt;p&gt;When creating a Lambda function you’ll be prompted to select a blueprint. Any will do, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;microservice-http-endpoint&lt;/code&gt; will most closely mirror the functionality of our Lambda. With a blueprint selected, you’ll need to configure the name, runtime, handler (entry point), IAM role, memory, and timeout for the Lambda. You will likely need to create a basic IAM role to allow your Lambda (the AWS console will help you do this), and you’ll want to decrease the memory requirement to 128MB.&lt;/p&gt;

&lt;p&gt;The entry point for the Lambda in this example is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.pingdomToHipchat&lt;/code&gt;, and per the Lambda spec the function takes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;event&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt; objects. The &lt;a href=&quot;https://github.com/lanyonm/aws-lambda-webhook&quot;&gt;full repo is on GitHub&lt;/a&gt;, and I’ve included the &lt;a href=&quot;https://github.com/lanyonm/aws-lambda-webhook/blob/master/index.js&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.js&lt;/code&gt;&lt;/a&gt; below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;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
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use strict&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./config.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pingdomToHipchat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// the contents of event is dependent on the configuration of API Gateway&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// console.log(&apos;the message is&apos;, event.message);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// there some things that the decodeURI method doesn&apos;t clean up for us&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;decodeURI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\+&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/%3A/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/%2C/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;the message json is:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hc_msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; is &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message_format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;hipchat message:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hc_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;api.hipchat.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/v2/room/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hipchat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;room&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/notification?auth_token=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hipchat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;http_opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setEncoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;BODY:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;statusCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;204&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;success - message delivered to hipchat&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;succeed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;message delivered to hipchat&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;failed with&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;hipchat API returned an error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;problem with request:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;failed to deliver message to hipchat&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hc_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On line 11 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;event.message&lt;/code&gt; is decoded and then cleaned further to compensate for the remaining encoding weirdness. Once I figured out how to translate the querystring params in API Gateway, iterating on this was the last bit of magic to get a json representation of the Pingdom alert in the Lambda function. The rest of the code creates the &lt;a href=&quot;https://www.hipchat.com/docs/apiv2/method/send_room_notification&quot;&gt;HipChat notification API&lt;/a&gt; request &amp;amp; payload, and then sends the request.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log&lt;/code&gt; statements are sent to CloudWatch - which can help you audit or debug during development. The built-in Lambda test functionality is also captures this output, and is the quickest way to ensure that changes to the Lambda function as expected. This is the test event for the Lambda above:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;requestId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;6645779c-18a9-473d-808e-2b74450c7347&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%7B%22check%22%3A%20%221834565%22%2C%20%22checkname%22%3A%20%22just%20a%20test%22%2C%20%22host%22%3A%20%22www.example.com%22%2C%20%22action%22%3A%20%22notify_of_close%22%2C%20%22incidentid%22%3A%208765%2C%20%22description%22%3A%20%22up%22%7D&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lastly, if you want to be able to copy/paste this code into the AWS Lambda console for testing you’ll need to remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.js&lt;/code&gt; require on line 4 and replace the two values on line 27 with your HipChat token and room id. More on finding these values for your setup &lt;a href=&quot;#prepping-hipchat&quot;&gt;below&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have the Lambda squared away, let’s see how it gets wired up with the API Gateway.&lt;/p&gt;

&lt;h2 id=&quot;aws-api-gateway&quot;&gt;AWS API Gateway&lt;/h2&gt;
&lt;p&gt;The API Gateway takes the Pingdom GET request and populates the event object passed to the Lambda. This step would be much easier if the Pingdom webhook used POST instead of GET, but you’ll learn something interesting about the API Gateway as a result.&lt;/p&gt;

&lt;p&gt;Inside the API Gateway console create a new API, create a resource, and create a GET method. When creating the method, you’ll need to select “Lambda Function” as the integration type the region the Lambda function is deployed into, and the Lambda name (which will auto-complete).&lt;/p&gt;

&lt;p&gt;At this point you should see something like this:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-api-gateway-method.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-api-gateway-method.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;The API Gateway method before configuration.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;There’s a couple things we need to do to translate the incoming Pingdom GET into the event that the Lambda expects. The first is to define the method request. We need to specify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Request-Id&lt;/code&gt; header and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt; query string as shown below:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-api-gateway-method-request.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-api-gateway-method-request.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;The API Gateway method request configuration.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The magic happens in the integration request configuration. Select “Lambda Function” for the integration type, select your Lambda function name, and add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application/json&lt;/code&gt; content-type Mapping Template. The previous step made &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Request-Id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt; available to be mapped into the event as follows:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-api-gateway-integration-request.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-api-gateway-integration-request.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;The API Gateway method integration request configuration.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Header parameters and query string parameters are both fetched via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;$input.params(&apos;key&apos;)&quot;&lt;/code&gt; function. Due to the encoding of the Pingdom webhook, we need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlEncode&lt;/code&gt; the message value to avoid a parse exception.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;requestId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;$input.params(&apos;X-Request-Id&apos;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;$util.urlEncode($input.params(&apos;message&apos;))&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once all this is configured, you’ll need to “Deploy API”. Stages are used as environments, so you can test API changes as they roll from development to production. If you make changes you’ll need to redeploy the API. Assuming your production environment is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prod&lt;/code&gt;, you’ll receive a url like &lt;a href=&quot;https://1x1x1x1x1x.execute-api.us-east-1.amazonaws.com/prod/pingdom-webhook&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://1x1x1x1x1x.execute-api.us-east-1.amazonaws.com/prod/pingdom-webhook&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;prepping-hipchat&quot;&gt;Prepping HipChat&lt;/h2&gt;
&lt;p&gt;HipChat’s v2 API requires you to create an integration to push notifications to rooms. This is done via the HipChat admin console and creates an authentication token for the room id specified.&lt;/p&gt;

&lt;p&gt;If you clone &lt;a href=&quot;https://github.com/lanyonm/aws-lambda-webhook&quot;&gt;the repo&lt;/a&gt;, you’ll want to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp config.js.sample config.js&lt;/code&gt; and add your token and room id to the config:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;hipchat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;room&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1111111&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can find more detail about how to package multiple files for upload to a Lambda function in the &lt;a href=&quot;https://github.com/lanyonm/aws-lambda-webhook&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;If all goes well, you should be able to issue &lt;a href=&quot;#testing-with-curl&quot;&gt;curl command&lt;/a&gt; above and see a notification appear in the configured HipChat room. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;down&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up&lt;/code&gt; in the description to see red and green highlighted messages:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-notification.png&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/pingdom-hipchat-notification.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;What you should see in HipChat if everything goes well.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;In Pingdom you’ll need to go into Alerting &amp;gt; Alerting Endpoints and add a webhook contact method to an Alerting Endpoint used by an Alert Policy that is used by the check you’d like to see in HipChat. You likely already have an Alert Policy used for your check, so adding an additional endpoint for that policy should be straightforward.&lt;/p&gt;

&lt;p&gt;I hope this works for you, and please let me know if it doesn’t! Big thanks to &lt;a href=&quot;https://twitter.com/ripienaar&quot;&gt;@ripienaar&lt;/a&gt; for his post on &lt;a href=&quot;https://www.devco.net/archives/2015/08/13/translating-webhooks-with-aws-api-gateway-and-lambda.php&quot;&gt;translating webhooks&lt;/a&gt; that got me thinking about this in the first place.&lt;/p&gt;
</description>
    <pubDate>Wed, 25 Nov 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/11/25/pingdom-hipchat-integration-aws-lambda.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/11/25/pingdom-hipchat-integration-aws-lambda.html</guid>
  </item>
  
  <item>
    <title>ChatOps: Hubot Grafana Images in HipChat</title>
    <description>&lt;p&gt;As we continue toward ChatOps and making our work visible at work, the next phase of maturing our monitoring systems is to create a query-able interface to our visualization system (Grafana) from HipChat. Grafana is a system I’ve become quite fond of and helped author the Chef cookbook for. The HTTP API for Grafana has matured, and the time seemed right to create this integration.&lt;/p&gt;

&lt;h2 id=&quot;high-level-design&quot;&gt;High-level Design&lt;/h2&gt;
&lt;p&gt;Having read about &lt;a href=&quot;http://blog.librato.com/posts/confessions-of-a-chatbot&quot;&gt;Librato’s ChatOps&lt;/a&gt; and seen &lt;a href=&quot;https://github.com/etsy/nagios-herald&quot;&gt;Etsy’s nagios-herald&lt;/a&gt;, I had a rough idea of the user experience I wanted. With a head full of hindsight bias, here are some of the requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A user-friendly query interface in chat (no magic numbers, server-specific names, etc.)&lt;/li&gt;
  &lt;li&gt;Images posted should be available in chat without additional authentication&lt;/li&gt;
  &lt;li&gt;Able to utilize our existing Grafana server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was delighted to find that Stephen Yeargin had already written &lt;a href=&quot;https://github.com/stephenyeargin/hubot-grafana&quot;&gt;hubot-grafana&lt;/a&gt;, a script that did all the heavy lifting for the first requirement. The Grafana docs site also has a &lt;a href=&quot;http://docs.grafana.org/tutorials/hubot_howto/&quot;&gt;how to integrate Hubot with Grafana&lt;/a&gt; article. Stephen’s  hubot script provides for discovery of dashboards, per-panel queries, template variables, and time-range queries. It’s really quite fantastic. However, it assumes that S3 will be used to host the images. While that’ll work for most folks (and certainly could work for us), I wanted to be able to use our existing Grafana server to house this integration. To achieve this I had to modify &lt;a href=&quot;https://github.com/criticalmass/hubot-grafana/blob/master/src/grafana.coffee&quot;&gt;grafana.coffee&lt;/a&gt;. More on that below.&lt;/p&gt;

&lt;p&gt;The default configuration provided by the &lt;a href=&quot;https://github.com/JonathanTron/chef-grafana&quot;&gt;chef-grafana&lt;/a&gt; cookbook includes Nginx as a proxy for grafana-server. For work we wrap the community cookbook to configure TLS, LDAP, and Grafana’s datasources. It seemed like a natural extension of visualization’s responsibility to have a small app on the Grafana node that can fetch/save rendered panel images and then use Nginx to serve those images. I called that small application &lt;a href=&quot;https://github.com/lanyonm/grafana-images&quot;&gt;grafana-images&lt;/a&gt;. More on that below as well.&lt;/p&gt;

&lt;h2 id=&quot;modifications-to-hubot-grafana&quot;&gt;Modifications to hubot-grafana&lt;/h2&gt;
&lt;p&gt;As mentioned above, I had to modify the &lt;a href=&quot;https://github.com/criticalmass/hubot-grafana&quot;&gt;hubot-grafana&lt;/a&gt; script to provide an alternate image persistence method (alternative to S3). The coffeescript additions are relatively straightforward:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffeescript&quot; data-lang=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;customFetchAndUpload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;requestHeaders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;grafana_api_key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/json&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;req_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;grafana_images_host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/grafana-images&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;imageUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# post to grafana-images&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req_opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;robot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;grafana-images POST: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req_opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, content-type[&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;content-type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;statusCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;sendRobotResponse&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;pubImg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;robot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;robot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Upload Error Code: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - [Access Error] - &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The Grafana API key is provided to the script by an environment variable and the newly added environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HUBOT_USE_GRAFANA_IMAGES&lt;/code&gt; determines whether or not to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customFetchAndUpload&lt;/code&gt; code-path. The full diff can be found &lt;a href=&quot;https://github.com/criticalmass/hubot-grafana/commit/a802a816b21f61b1cf4d61f0f4967fcd97fb9bf8#diff-8387f8787e3a34b1530cc6f1a1ff23e2&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you can see, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/grafana-images&lt;/code&gt; uri is hard-coded. That’s because the route used by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; is hard-coded. Also, note that the necessary authentication token is passed along with the json payload. In many ways this function is treating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; as a proxy for Grafana.&lt;/p&gt;

&lt;p&gt;Another addition to note is the help text I added to the hubot script. You can ask the bot “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;graf help&lt;/code&gt;” and it’ll respond with increasingly complex query samples. Yay for user friendliness!&lt;/p&gt;

&lt;h2 id=&quot;grafana-images&quot;&gt;grafana-images&lt;/h2&gt;
&lt;p&gt;Following my experience with &lt;a href=&quot;/articles/2015/03/29/golang-http-stats-collector.html&quot;&gt;http-stats-collector&lt;/a&gt;, Golang seemed like a good choice for the small application. It acts as a proxy and therefore expects only two things: a valid API token and json payload containing the full Grafana panel render url. To give more context to what’s happening, here’s an http call diagram:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://blog.lanyonm.org/images/grafana-images-diagram.svg&quot;&gt;&lt;img src=&quot;https://blog.lanyonm.org/images/grafana-images-diagram.svg&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;The http calls required for hubot-grafana using grafana-images numbered by sequence&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customFetchAndUpload&lt;/code&gt; function described above is call #4. From there &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; will fetch (#5 &amp;amp; #6), save, and return a sharable image url via json (#7). Here’s a snippet from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt;’ &lt;a href=&quot;https://github.com/lanyonm/grafana-images/blob/master/handlers.go&quot;&gt;handlers.go&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c&quot;&gt;// Fetch image&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Accept&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http.Get -&amp;gt; %v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ioutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ioutil.ReadAll -&amp;gt; %v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Save image&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%x.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ioutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%s/%s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imagePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0666&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ioutil.WriteFile -&amp;gt; %v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Return image location&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pubImg&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s/%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are several variables assumed to be set:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt; - the requested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imageUrl&lt;/code&gt; from the json&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;token&lt;/code&gt; - the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imagePath&lt;/code&gt; - a path on disk to store the saved images&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imageHost&lt;/code&gt; - the host used in the building the json response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything is configured correctly, the Grafana dashboard panel will be saved to disk and the json sent back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hubot-grafana&lt;/code&gt;. Further detail can be found &lt;a href=&quot;https://github.com/lanyonm/grafana-images&quot;&gt;on GitHub&lt;/a&gt;. I tired to make all the error messages helpful and actionable, but if you find an error condition that isn’t well explained, please open a GitHub issue.&lt;/p&gt;

&lt;h3 id=&quot;security&quot;&gt;Security&lt;/h3&gt;
&lt;p&gt;You may have noticed that the app very simply downloads whatever is specified at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imageUrl&lt;/code&gt; and saves it as a png. This can be dangerous given that nothing checks to ensure that the contents are in-fact an image and not an exploit. &lt;em&gt;Take care&lt;/em&gt; to only allow specific traffic to make requests of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt;. I may add a check via Golang’s png package to ensure proper encoding, but it may be quite some time before that happens (pull requests welcome).&lt;/p&gt;

&lt;h2 id=&quot;nginx-config&quot;&gt;Nginx Config&lt;/h2&gt;
&lt;p&gt;As mentioned above, I used Nginx to proxy grafana-server. I also use it to proxy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; and serve the saved panel images. Here’s a sample conf that should would for this purpose:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# the grafana-server config has been omitted&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;grafana-images&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;127.0.0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# excerpt for grafana-images&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/grafana-images&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://grafana-images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.0.0.11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# ip of server running hubot&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;deny&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/saved-images&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imageHost&lt;/code&gt; passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; is the FQDN &lt;em&gt;plus&lt;/em&gt; the location of the saved images. The value used will be dependent on the web server hosting the saved images.&lt;/p&gt;

&lt;h2 id=&quot;other-uses&quot;&gt;Other Uses&lt;/h2&gt;
&lt;p&gt;Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; exposes its functionality over a simple HTTP API, expanding its purpose should be straightforward. The app expects an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Authorization: Bearer grafana-token-goes-here&quot;&lt;/code&gt; header and a json payload:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;imageUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://grafana.example.com/render/dashboard-solo/db/sample-dashboard/?panelId=5&amp;amp;width=1000&amp;amp;height=500&amp;amp;from=now-6h&amp;amp;to=now&amp;amp;var-server=test-server&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;sensu-notifications&quot;&gt;Sensu Notifications&lt;/h3&gt;
&lt;p&gt;At work we have incorporated Grafana panel image embedding functionality into our Sensu HipChat handler. We started with the &lt;a href=&quot;https://raw.githubusercontent.com/sensu/sensu-community-plugins/e2286b69eda081b4c59226667245e95f4c3a45e1/handlers/notification/hipchat.rb&quot;&gt;Sensu community HipChat handler&lt;/a&gt; and modified the message body heavily for our purposes.&lt;/p&gt;

&lt;p&gt;The code to add Grafana panel images to Sensu HipChat notifications is roughly:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;check&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;graph_image&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# do some work to get the static image from grafana-images&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;https://grafana.example.com/grafana-images&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_ssl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/grafana-images&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Content-Type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;application/json;charset=utf-8&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Authorization&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Bearer grafana-token-goes-here&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;imageUrl&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;check&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;graph_image&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;amp;from=now-6h&amp;amp;to=now&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_json&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;public_image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;pubImg&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;br /&amp;gt;&amp;lt;a href=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;public_image&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt;&amp;lt;img src=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;public_image&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;br /&amp;gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; - [graph_image fetch failed (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)]&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@event[&apos;check&apos;][&apos;graph_image&apos;]&lt;/code&gt; value is assumed to be a valid dashboard panel render url &lt;em&gt;without&lt;/em&gt; the from/to times: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://grafana.example.com/render/dashboard-solo/db/sample-dashboard/?panelId=5&amp;amp;var-server=test-server&amp;amp;width=1000&amp;amp;height=500&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;panelId&lt;/code&gt; is can be obtained from the UI of the dashboard.&lt;/p&gt;

&lt;p&gt;We manage our infrastructure with Chef and it creates all the Sensu checks, thus allowing us to programmatically build the checks. We add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;graph_image&lt;/code&gt; attribute to the check that contains a panel render url associated with the metric(s) that can help provide context to the Sensu notification. Chef can give the FQDN of the Grafana node as well as the values for template attributes, so it all comes together quite cleanly.&lt;/p&gt;

&lt;h2 id=&quot;other-considerations&quot;&gt;Other Considerations&lt;/h2&gt;
&lt;p&gt;One thing not handled by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana-images&lt;/code&gt; is saved image retention. You’ll need to create a purge policy that works for you. Once I’ve figured out how we’re going to handle that, I’ll add it here. :)&lt;/p&gt;
</description>
    <pubDate>Wed, 30 Sep 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/09/30/chatops-hubot-grafana-images-hipchat.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/09/30/chatops-hubot-grafana-images-hipchat.html</guid>
  </item>
  
  <item>
    <title>A Participant&apos;s Conference - DevOpsDays Chicago 2015</title>
    <description>&lt;div class=&quot;right&quot; style=&quot;max-width:331px;&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;http://www.devopsdays.org/events/2015-chicago/&quot;&gt;&lt;img title=&quot;DevOpsDays Chicago 2015&quot; src=&quot;/images/devopsdays-chicago-2015.png&quot; /&gt;&lt;/a&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Last week was DevOpsDays Chicago 2015, my second DevOpsDays Chicago as a co-organizer. We learned a lot in our first year, and I feel that we improved substantially this time around. The anecdotal feedback over the past several days has been overwhelmingly positive, and it’s bolstering to feel that outpouring of appreciation after putting in so much effort. My experience as an organizer was much improved as well. We avoided &lt;a href=&quot;https://en.wikipedia.org/wiki/Parkinson&apos;s_law_of_triviality&quot;&gt;bike-shedding&lt;/a&gt; as much as possible, more clearly divided responsibilities, and did our best to improve on our shortcomings in 2014.&lt;/p&gt;

&lt;p&gt;There are most certainly aspects of the event that we can and will continue to improve upon. We have started the post-mortem process and will be publishing both the 2015 and 2014 post-mortem documents. Keep an eye out for that.&lt;/p&gt;

&lt;p&gt;I wasn’t planning on typing up my thoughts about the conference, but &lt;a href=&quot;http://www.carolynvanslyck.com/blog/2015/08/devops-days-chicago-2015/&quot;&gt;a post&lt;/a&gt; by Carolyn Van Slyck has been on my mind…&lt;/p&gt;

&lt;h2 id=&quot;participation&quot;&gt;Participation&lt;/h2&gt;

&lt;div class=&quot;center quote&quot;&gt;
  &quot;DevOpsDays is a participant&apos;s conference&quot;
&lt;/div&gt;

&lt;p&gt;I’m not sure where I picked up the idea, but I recall saying it in our organizer Slack channel during the run-up to the 2014 event. It is not a platitude. It is at the heart of what makes DevOpsDays an amazing conference series. Open Spaces are typically a new experience for first-time participants and sometimes even dreaded due to the apparent chaos of unstructured time, but every anecdote I heard this year about Open Spaces was it being the best part of the conference.&lt;/p&gt;

&lt;div class=&quot;right&quot; style=&quot;max-width:300px;&quot;&gt;
  &lt;figure style=&quot;margin:10px 10px 0px 10px;&quot;&gt;
    &lt;a href=&quot;/images/devopsdays-chicago-2015-badge.jpg&quot;&gt;&lt;img title=&quot;DevOpsDays Chicago 2015 Participant Badge&quot; src=&quot;/images/devopsdays-chicago-2015-badge.jpg&quot; /&gt;&lt;/a&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;It was my honor and responsibility this year to open the conference. It was with incredible pride that I was able to ask everyone to look down at their badge and see that they are a &lt;em&gt;participant&lt;/em&gt; at DevOpsDays, to say that the DevOpsDays community values respect, inclusiveness, and diversity, to say that DevOpsDays is a safe space and remind everyone to be cognizant, and finally to say that whether or not they realized it at the time, their contributions to the community would be valued and respected.&lt;/p&gt;

&lt;p&gt;These statements are backed by our &lt;a href=&quot;http://www.devopsdays.org/events/2015-chicago/conduct/&quot;&gt;Code Of Conduct&lt;/a&gt;. Originally introduced by DevOpsDays Pittsburgh, this code has become the template used by DevOpsDays events. It conveys a strong belief that is strongly held. As a participant I have spoken up when I have felt the code is violated. As an organizer, it was and is a guide for the tone to set.&lt;/p&gt;

&lt;p&gt;I also introduced Open Spaces, and while I’ll never hold a candle to &lt;a href=&quot;https://twitter.com/patrickdebois&quot;&gt;Patrick’s&lt;/a&gt; introduction I tried to extend the tone of respect and inclusiveness into the four principles of Open Spaces:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Whoever comes is the right people&lt;/li&gt;
  &lt;li&gt;Whatever happens is the only thing that could have&lt;/li&gt;
  &lt;li&gt;Whenever it starts is the right time&lt;/li&gt;
  &lt;li&gt;When it’s over, it’s over&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I heard these statements repeated throughout the afternoon as if to confirm that it is indeed okay to leave an Open Space you’re no longer engaged with or continue an Open Space that isn’t over. Yes, absolutely.&lt;/p&gt;

&lt;h2 id=&quot;reflection&quot;&gt;Reflection&lt;/h2&gt;
&lt;p&gt;Experiences like Carolyn’s are what I and my co-organizers had hoped to nurture. A supportive community that isn’t just &lt;em&gt;open to&lt;/em&gt; dialog, but actually encourages communication and sharing. At the end of DevOpsDays Chicago 2014, I felt like I’d submitted a huge PR to critical OSS that I’d been using for years - exhausted, but happy. This year, I feel pride of stewardship and for being part of something that is helping make peoples’ lives better.&lt;/p&gt;

&lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;/h2&gt;
&lt;p&gt;I’d like to thank the DevOpsDays Chicago 2015 participants, speakers, sponsors, volunteers, and support folks. The community that we convene for just a couple days is why I wanted to organize once again this year. I’d also like to thank my co-organizers. It wouldn’t be nearly as much fun if you weren’t all awesome.&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;https://pbs.twimg.com/media/CNYB0wJUAAAFy3P.jpg&quot;&gt;&lt;img src=&quot;https://pbs.twimg.com/media/CNYB0wJUAAAFy3P.jpg&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;The DevOpsDays Chicago 2015 Organizing Team&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
</description>
    <pubDate>Tue, 01 Sep 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/09/01/a-participants-conference-devopsdays-chicago.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/09/01/a-participants-conference-devopsdays-chicago.html</guid>
  </item>
  
  <item>
    <title>Web Performance Monitoring - DevOpsDays MSP 2015</title>
    <description>&lt;p&gt;I initially intended to speak about how we use configuration management to automate our real user measurement (RUM), but as I was putting together my talk I discovered that the real insight was how the same tools can be used to facilitate the front-end developer’s relationship with prod.&lt;/p&gt;

&lt;p&gt;I opened the Ignite by speaking about the lack of operational visibility into the end user’s experience and how our initial attempt to close this gap was to use the Navigation Timing data. We use a Golang program called &lt;a href=&quot;/articles/2015/03/29/golang-http-stats-collector.html&quot;&gt;http-stats-collector&lt;/a&gt; to collect NavTiming data as well as javascript errors and CSP reports. Looking at the data after it was processed by our monitoring pipeline, we saw that the real insight was how javascript errors could tell the story of the end user’s experience - something NavTiming data did not do for us.&lt;/p&gt;

&lt;p&gt;I concluded the talk with a sampling of the systems and templates we have in place to facilitate the collection of data. While this understanding of the end user experience is valuable, the data collection and visualization needs to be turn-key for it to be used across our organization.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://speakerdeck.com/lanyonm/web-performance-monitoring&quot;&gt;https://speakerdeck.com/lanyonm/web-performance-monitoring&lt;/a&gt;&lt;/p&gt;

&lt;script async=&quot;&quot; class=&quot;speakerdeck-embed&quot; data-id=&quot;932c1c5a6d2c4ed899a19893908bb64f&quot; data-ratio=&quot;1.77777777777778&quot; src=&quot;https://speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;&lt;a href=&quot;https://youtu.be/ku3O4HnMXrM?t=395&quot;&gt;https://youtu.be/ku3O4HnMXrM?t=395&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;embed-container&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/ku3O4HnMXrM?t=400&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;All photo credits including those in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;og:metadata&lt;/code&gt; go to &lt;a href=&quot;https://twitter.com/bridgetkromhout&quot;&gt;Bridget Kromhout&lt;/a&gt; or DevOpsDays Minneapolis 2015.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s a transcript of the talk:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Hi, my name is Mike Lanyon, and I work at a digital ad agency called Critical Mass. I would like to talk about web performance monitoring. In DevOps we often talk about monitoring of the systems that provide experiences, but not the monitoring of the end user’s experience itself.&lt;/li&gt;
  &lt;li&gt;Critical Mass is a digital experience design agency. That means that we put the customer at the center of our process, through strategy, design, technology and analytics. We want to use our influence with clients to make their customers’ lives better.&lt;/li&gt;
  &lt;li&gt;More specifically than web performance, I’d like to talk about a front end developer’s relationship to prod. In years past this relationship may have been through an FTP client, but no-matter the technology there was a gap in operational visibility.&lt;/li&gt;
  &lt;li&gt;The front-end developer’s preparation for production has gotten quite robust. Run sass, concat &amp;amp; minify js. Maybe there’s a CDN, but then it gets a bit fuzzier… Where is the operational visibility in this?&lt;/li&gt;
  &lt;li&gt;WebPage test is a tool our teams use to quantify the end user’s experience. This chart shows the composite Speed Index of one of our web pages. The Index is the integral of the space above the charted lines, which represent the perceived completion of the page load.&lt;/li&gt;
  &lt;li&gt;But still there is no operational visibility. There’s no way for a web developer on my team to understand the performance of a user currently visiting our site. There’s nothing analogous to the app log with a stack trace detailing the user’s crummy experience.&lt;/li&gt;
  &lt;li&gt;RUM - real user monitoring. This is something that began to pop up a few years ago. This is different than synthetic monitoring because you’re collecting data from the experience of &lt;em&gt;real&lt;/em&gt; users.&lt;/li&gt;
  &lt;li&gt;Navigation Timing API is one of the most well established means to collecting the real user’s performance. There are several solutions that will capture this information for you like Google analytics or NewRelic.&lt;/li&gt;
  &lt;li&gt;Golang. I toyed around with Go a couple years ago, and didn’t really understand how it could be useful to me at the time - but it’s reputation for being fast seemed like a good fit for collecting RUM performance data.&lt;/li&gt;
  &lt;li&gt;I created https-stats-collector. Inspired by a GDSTeam project called event-store that saves content security policy reports. I don’t really want to focus much on the Golang code here - only to highlight that it’s open source and would love to have some feedback on the project.&lt;/li&gt;
  &lt;li&gt;There are three primary routes offered by the application: nav-timing, js-error, and csp-report. In addition to nav-timing, I’ll talk about js-errors, which is fed by a global javascript error and logs the errors experienced by users.&lt;/li&gt;
  &lt;li&gt;As you can see, after nav-timing data is processed by our monitoring pipeline we’re able to create pretty, squiggly line graphs. I really enjoy these, and I think they’re great, but they don’t help tell the end user’s story.&lt;/li&gt;
  &lt;li&gt;This is screencap of Kibana showing the errors captured by the js-errors handler and processed by our ELK stack. When one of the senior developers first saw this, he pointed to a cluster of errors experienced by a single user and said, “I wonder what the story is there”.&lt;/li&gt;
  &lt;li&gt;We have found that while nav-timing data is really cool, javascript error reporting is the game changer. It gave the teams a level of traceability they’d never had before and equated to a front-end version of the app log.&lt;/li&gt;
  &lt;li&gt;This capability is really valuable, but it has to be operationalized and become part of the default path or path of least resistance.&lt;/li&gt;
  &lt;li&gt;We use Chef to put all the bits in place, http-stats-collector, statsd, logstash, elasticsearch, influxdb, kibana, grafana, etc. Configuration management also helps us standardize and scale the implementation.&lt;/li&gt;
  &lt;li&gt;Sample implementations that are framework agnostic help the client/product teams adopt the pattern and integrate it into their work.&lt;/li&gt;
  &lt;li&gt;We have gone so far as to make these RUM collection tools part of our standard stack. In this example a team asked for a simple webserver, and they got the webserver - but packed with all the data collection bits pre-installed.&lt;/li&gt;
  &lt;li&gt;If you take anything from this talk, please consider how to help create the connection between your technology teams and your users. Give your teams the tools to make this connection with prod - to form this relationship with the individual end user.&lt;/li&gt;
  &lt;li&gt;Thank you&lt;/li&gt;
&lt;/ol&gt;
</description>
    <pubDate>Wed, 08 Jul 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/speaking/2015/07/08/web-performance-monitoring-devopsdays-minneapolis.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/speaking/2015/07/08/web-performance-monitoring-devopsdays-minneapolis.html</guid>
  </item>
  
  <item>
    <title>Grafana Chef Cookbook Wrapper to Override Nginx SSL Template</title>
    <description>&lt;p&gt;I recently contributed to the overhaul and 2.0 release of the &lt;a href=&quot;https://supermarket.chef.io/cookbooks/grafana&quot;&gt;Grafana Chef cookbook&lt;/a&gt;. It was a nearly complete rewrite of the 1.x version, and many decisions were made along the way about what should (and should not) be included in the effort. The cookbook is designed to be as flexible as possible via attributes and to provide the user with a functional setup using the defaults. The previous version used Nginx as a web server, and it made sense to proxy the new Grafana with Nginx in the default setup.&lt;/p&gt;

&lt;p&gt;One of the initially identified tasks was to provide SSL by default within the cookbook, but that proved to be foolish for two reasons: 1) it was well outside the scope of the Grafana cookbook and 2) SSL configs are highly dependent on several diverse factors ranging from web server version to client browser requirements. Creating a default Nginx SSL setup that was flexibly configurable with attributes was duly marked as out of scope, &lt;a href=&quot;https://github.com/JonathanTron/chef-grafana/issues/47&quot;&gt;but not without some discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For reference, here’s the default Nginx template from the Grafana cookbook:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/etc/nginx/sites-available/grafana&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;nginx&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;template&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cookbook&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;nginx&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;template_cookbook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;notifies&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;service[nginx]&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0644&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;root&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;root&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;grafana_port: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ini&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;server&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;http_port&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;server_name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_hostname&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;server_aliases: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_aliases&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;listen_address: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_listen&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;listen_port: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_port&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;overriding-the-nginx-conf-template&quot;&gt;Overriding The Nginx Conf Template&lt;/h2&gt;
&lt;p&gt;When defining an Nginx template with SSL enabled, it’s helpful to have additional variables passed to the template so info like cert locations can be more flexibly defined. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cookbook&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; attributes provided by the recipe would allow for the wrapper cookbook to define a new template, but &lt;em&gt;not&lt;/em&gt; allow for additional variables to be passed to the template. By taking advantage of Chef’s &lt;a href=&quot;https://docs.chef.io/chef_client.html#the-chef-client-title-run&quot;&gt;compile phase&lt;/a&gt;, we can alter the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template[&apos;/etc/nginx/sites-available/grafana&apos;]&lt;/code&gt; resource to not only use the template of our choosing, but also pass additional attributes to the resource.&lt;/p&gt;

&lt;p&gt;The resource definition below is from a Grafana cookbook wrapper recipe:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;template: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/etc/nginx/sites-available/grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cookbook&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wrapper-cookbook&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;default/grafana/nginx.conf.erb&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;mo&quot;&gt;0644&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;grafana_port: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ini&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;server&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;http_port&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;server_name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_hostname&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;server_aliases: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_aliases&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;listen_address: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_listen&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;listen_port: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;grafana&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;webserver_port&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ssl_cert_dir: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;wrapper-cookbook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ssl&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;cert_dir&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ssl_cert_name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;wrapper-cookbook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ssl&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;cert_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ssl_key_dir: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;wrapper-cookbook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ssl&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key_dir&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ssl_key_name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;wrapper-cookbook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ssl&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ssl_dhparam_name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;wrapper-cookbook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ssl&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dhparam&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notifies&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;service[nginx]&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:immediately&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Chef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ResourceNotFound&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Chef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;warn&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;could not find template to override!&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The recipe is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grafana&lt;/code&gt; and the containing cookbook is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrapper-cookbook&lt;/code&gt;. When compared to the template within the Grafana cookbook’s &lt;a href=&quot;https://github.com/JonathanTron/chef-grafana/blob/v2.0.0/recipes/_nginx.rb#L22-L36&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_nginx.rb&lt;/code&gt;&lt;/a&gt;, you can see that five additional SSL-related attributes are passed to the template (lines 12-16).&lt;/p&gt;

&lt;h2 id=&quot;a-sample-nginx-ssl-template&quot;&gt;A Sample Nginx SSL Template&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx.conf.erb&lt;/code&gt; will be dependent on your SSL requirements, but here’s an example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;#
# This file was generated by Chef for &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;fqdn&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;.
# Do not modify this file by hand!
#

upstream grafana {
  server 127.0.0.1:&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@grafana_port&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;
}

server {
  listen                &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@listen_address&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@listen_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@listen_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@listen_port&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;

  server_name           &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@server_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@server_aliases&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;
  access_log            /var/log/nginx/&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@server_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;.access.log;

  ssl on;
  ssl_certificate &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ssl_cert_dir&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;/&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ssl_cert_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;
  ssl_certificate_key &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ssl_key_dir&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;/&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ssl_key_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;
  ssl_dhparam &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ssl_cert_dir&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;/&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ssl_dhparam_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;

  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers &apos;AES256+EECDH:AES256+EDH&apos;;
  ssl_prefer_server_ciphers on;

  add_header Strict-Transport-Security max-age=31536000;

  ssl_stapling on;
  ssl_stapling_verify on;

  location / {
    proxy_pass http://grafana;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Your mileage will most certainly vary. If you’re looking for suggestions on what SSL configs to use Mozilla has put together a great TLS configuration generator: &lt;a href=&quot;https://mozilla.github.io/server-side-tls/ssl-config-generator/&quot;&gt;https://mozilla.github.io/server-side-tls/ssl-config-generator/&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You could argue that this is unnecessary for the Grafana cookbook’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_nginx.rb&lt;/code&gt; given that the recipe is so simple, but it illustrates the power of Chef’s compile phase to override the defaults of the cookbooks used.&lt;/p&gt;

&lt;p&gt;In addition to a default Nginx setup, the cookbook provides LWRPs for creating datasources, dashboards, organizations, and users. It’ll be exciting to see how people use and extend it.&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;figure&gt;
    &lt;a href=&quot;/images/grafana-2.png&quot;&gt;&lt;img src=&quot;/images/grafana-2.png&quot; /&gt;&lt;/a&gt;
    &lt;figcaption&gt;Sample Dashboard configured with the Grafana cookbook.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
</description>
    <pubDate>Sun, 28 Jun 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/articles/2015/06/28/grafana-chef-cookbook-nginx-ssl.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/articles/2015/06/28/grafana-chef-cookbook-nginx-ssl.html</guid>
  </item>
  
  <item>
    <title>Innovation in a Creative Company - DevOpsDays NYC 2015</title>
    <description>&lt;p&gt;I had the privilege of giving an Ignite at DevOpsDays New York 2015 this past week. The theme of the event was inclusivity, complexity, and empathy, which really made for a great event.&lt;/p&gt;

&lt;p&gt;My talk was about some of the successes and failures I’ve experienced when trying to bring DevOps values to my organization and explain how empathy, inclusion, and communication are integral to the successes. I discussed a handful of ideas, including surprise reduction, curiosity, and sharing, and briefly touched upon how diversity in teams enables stronger ideas and better innovation. The journey of cultural change will be different from one organization to the next, but I hope to ignite more conversation on this topic by talking about the changes we’ve attempted.&lt;/p&gt;

&lt;p&gt;Ultimately, by working to understand other disciplines and finding empathetic, inclusive ways to bridge the gaps between “us and them,” technology can work more collaboratively with other disciplines, and we can create better, more innovative products as a result.&lt;/p&gt;

&lt;script async=&quot;&quot; class=&quot;speakerdeck-embed&quot; data-id=&quot;4d5d690ca3f4440580aad0b9cabb6489&quot; data-ratio=&quot;1.77777777777778&quot; src=&quot;https://speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Here’s a transcript of the talk:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Hi, my name is Mike Lanyon, and I work at an ad agency called Critical Mass. We are a digital experience design agency with a relentless focus on the customer. We’re comprised of four primary disciplines: Strategy, Design, Technology and Marketing Science, and we work together to design experiences for large brands.&lt;/li&gt;
  &lt;li&gt;We are not a technology company, but as a digital agency, technology is essential to what we deliver. I like to say that software is the delivery mechanism for brand experiences. And our challenge is to have our disciplines work together in a way that delivers the most innovative yet intuitive experiences.&lt;/li&gt;
  &lt;li&gt;Creating a culture where the disciplines work together to innovate is difficult. Creating a Kata that we practice across the disciplines has been exceedingly difficult. I’m going to talk about some of the qualities that I think help lead us in the right direction.&lt;/li&gt;
  &lt;li&gt;If you’ve been part of the DevOps community for a while you’ll be familiar with lessons of Deming, Goldratt, and Shewhart. While I have found their teachings of statistical process control, system of profound knowledge and theory of constraints valuable, they alone are insufficient to unlocking innovation in our organization.&lt;/li&gt;
  &lt;li&gt;When we look at what drives and delivers value in our organization, we find communication, empathy and inclusivity. You may be thinking, “yeah, duh.” but I assure you that if it was as simple our theme at DevOpsDays New York wouldn’t be what it is.&lt;/li&gt;
  &lt;li&gt;One of the ways I look at communication is surprise reduction. People like to know what’s going on and what to expect. Secrecy is toxic. We’ve all experienced this, whether it’s not knowing enough context for an assignment or simply not knowing that a project is underway.&lt;/li&gt;
  &lt;li&gt;It seems intuitive to keep others in the loop, but surprise reduction is more than that. It’s about understanding what you already know that others may not, and anticipating their needs. This takes a level of effort that goes beyond common sense, but it’s well worth the energy.&lt;/li&gt;
  &lt;li&gt;When everyone is on the same page - it’s more likely that you’ll get diverse creative contribution from all disciplines. Innovation sprouts from this proactive communication.&lt;/li&gt;
  &lt;li&gt;I have found that curiosity is another key quality for nurturing innovation. When you’re curious you want to know how things work, you want to know what motivates people. I believe it to be an innate quality of intrinsically motivated people.&lt;/li&gt;
  &lt;li&gt;Curiosity has driven designers to learn how to use the web inspector. It’s driven information architects to understand how javascript and css power animations, and it drove me to learn about operations. These pieces on the screen where created by my cousin, Sarah Walker who happens to live and work in Brooklyn. These pieces were inspired by how technology has interwoven itself into our lives.&lt;/li&gt;
  &lt;li&gt;If there is one thing you take away from this talk, reflect on your curiosity and act on that interest. If you know someone with that talent or knowledge, ask them about it. Start a conversation.&lt;/li&gt;
  &lt;li&gt;Now put yourself on the other side of that interaction. When you’re in the position to answer questions or teach, please, please, please take the time to do so. Continuing the conversation will start a relationship that lasts longer than the dialog.&lt;/li&gt;
  &lt;li&gt;Early in my career, I didn’t value sharing. I wasn’t confident enough to share work-in-progress and documentation wasn’t valued until after it was needed. As I matured I came to understand how sharing enables the organization to gain knowledge as a whole.&lt;/li&gt;
  &lt;li&gt;I came to understand much more holistically that the creative process depends on sharing. I still struggle with how to keep the sharing feedback loop going within the creative process, but the Chicago voting mantra applies: do it early and often.&lt;/li&gt;
  &lt;li&gt;Communication, curiosity, and sharing ladder into a what I believe to be a higher order humanistic quality: empathy. Empathy is understanding and sharing in the feelings of another. And empathy is requisite to innovation. Good design is rooted in Empathy.&lt;/li&gt;
  &lt;li&gt;Imagine trying to design a product without understanding the motivation of the consumer - ok, that’s kinda unheard of, but consider the value of understanding and sharing in emotional needs of both your consumers and your team members.&lt;/li&gt;
  &lt;li&gt;At Critical Mass we not only consider how our software will be received by operators for example, but also think empathetically across discipline lines. Contextualizing a decision with an anecdote while not hiding the details helps the other disciplines relate to what we do in technology.&lt;/li&gt;
  &lt;li&gt;Inclusivity is not to be discounted. It too is requisite to innovation. Ensuring your culture is aware of exclusive behavior and discourages it will allow a more diverse set of contributors, which will ultimately lead to greater perspective and thereby innovation.&lt;/li&gt;
  &lt;li&gt;The creative process, supporting structures, and empathetic culture will never be available as a service or written as code. But we can most certainly apply the values that we hold dear in the DevOps community to this challenge and succeed.&lt;/li&gt;
  &lt;li&gt;Thank you.&lt;/li&gt;
&lt;/ol&gt;
</description>
    <pubDate>Thu, 30 Apr 2015 00:00:00 +0000</pubDate>
    <link>https://blog.lanyonm.org/speaking/2015/04/30/innovation-creative-company-devopsdays-newyork.html</link>
    <guid isPermaLink="true">https://blog.lanyonm.org/speaking/2015/04/30/innovation-creative-company-devopsdays-newyork.html</guid>
  </item>
  
  </channel>
</rss>
