Loggly by Hooverhttp://loggly.com/blog/2012-04-30T17:47:40-07:00Building Great Software...5 Cool Tips For Success! http://loggly.com/blog/2012/03/you-know/2012-03-15T16:08:03-07:002012-03-15T16:00:00-07:00<p><img alt="" src="/assets/4f5fc9ebdabe9d2fbc0014a5/cooldog.png" /></p>
<p>Writing software is hard. Writing it well is even harder. I have been rolling some ideas around in my head on how to make software that is better for the people creating it and the people using it. Software has some distinct (and not-so-distinct) phases that it goes through as you work on it. The first of those phases for developers is the design phase. Here are some ideas on making better software during the design process.</p>
<h3>If you start with "you know what would be cool?", kill the idea.</h3>
<p>OK, so maybe that's a little harsh, but that's my knee jerk reaction when I hear a "you know what would be cool" idea. Let me rephrase my suggestion; stop and think. You may be wondering what I have against doing cool things. Nothing. There's nothing wrong with doing cool things. The problem is that we developers gravitate toward using a cool piece of technology for the sake of using it rather than finding the best fit for our problem. We often attempt to solve a problem either using a cool new piece of technology in a way that it wasn't intended for, or new language features when an alternative would have been better suited. Redis, mongodb, rabbitmq, Python, Ruby, Clojure, Scala, Node.js are all great technologies that meet various needs in their own ways. Sometimes they may be the best fit for your problem, but not always. Just think twice when you think you're about to do something "cool". Developing software is less about being cool and more about building something that works well.</p>
<h3>Aim for simplicity.</h3>
<p>Software developers have a knack for building and stacking and inheriting and composing until the original simple solution is unrecognizable in a mound of clever engineering. When you're thinking about the next piece of code that you're going to write, start simple. Don't build something in your mind that is more complex than it has to be. You should always use the simplest thing that works, except for when you shouldn't. Next point.</p>
<h3>The simplest solution isn't always best.</h3>
<p>Sometimes the simplest thing just won't cut it. Sometimes you need that really weird class hierarchy or a really tricky algorithm or something that really hurts your brain to even think about. Usually this is going to happen on the second iteration of a problem. You should still always start with the simplest thing. If that doesn't seem to hold up, go on to something more complicated, but keep the complexity to a minimum.</p>
<h3>Don't build more than you need.</h3>
<p>It's often tempting to try to build a solution that handles every contingency or meets the needs a user doesn't even know what they have yet. When you are tempted to design everything into your application, think twice. Don't design what you don't need. If you can come up with a good reason for designing it in, then go for it. Otherwise, don't bother. Build just what you need and no more.</p>
<h3>Think long-term.</h3>
<p>I've seen so many developers (including myself) focus on getting something working now and neglect foreseeable issues with maintenance and deployment (which are the two major areas that this impacts). When you are thinking about the next piece of code that you are going to write, take the time to make sure you are focused on the long term solution. This may sound like it contradicts my previous idea of doing the simplest thing possible, but I think these ideas can co-exist. Start simple. Get something working. But don't plan on getting it "justworking"; plan on getting it working so that the next generation of developers that work on it will be happy you made the decisions you did rather than having them curse your name.</p>
<p>These aren't new thoughts. I've heard them stated by other experienced developers in various ways. And they're not immutable laws of nature. I think they're good guidelines, but the world won't come crashing down on your ears if you do something different. But I do believe that your experience of designing software will benefit from it. And as a result, your software will be better for you and your customer.</p>Brian SchroederIntroducing the Loggly Wood Shophttp://loggly.com/blog/2012/03/introducing-the-loggly-wood-shop2/2012-03-02T09:40:14-08:002012-03-02T08:35:00-08:00<p><img alt="" src="/assets/4f500d40dabe9d111b000b83/grayshiver2.png" /></p>
<p>We've been talking about doing a Loggly <a href="http://loggly.myshopify.com/">swag store</a> for a while, and I'm excited to announce it's finally here! Now everybody everywhere has access to our snazzy swag. We have some rocking t-shirts, stickers and beer glasses available for purchase. I'm not just saying this because I work here, when I wear Loggly t-shirts out on weekends people compliment me on them.</p>
<p>We’ve always made a point to produce swag that has lasting appeal. All of us have gone to conferences and collected a bunch of stuff, only to take it back to our room, look at it, and leave it there. We are proud to say that people actually wear our swag out and about, track us down at events specifically to get their hands on it, and tweet at us to ask about how they can get it. We've even been offered money for the shirts off our backs, no joke. We are incredibly lucky that the allure of our brand continues beyond our product. Even folks who’ve never used our product, and never will, love the branding and culture we’ve created and want to be a part of it. The Loggly store was born from this enthusiasm and support, and we’re extremely grateful for it. </p>
<p><img alt="" src="/assets/4f500ddadabe9d0cf40020e4/shivermetimberspintglasses.png" /></p>
<p>We’d like to give a special shout out to our fantastic illustrator <a href="andrejolicouer.com">Andre Jolicoeur</a> who has done an outstanding job bringing Hoover the beaver and the Loggly brand to life with his creations. We also owe a great deal of praise to <a href="shopify.com">Shopify</a>, whose service made it a breeze for us to get our store up and running. The <a href="http://wiki.shopify.com/Shopify_Textmate_Bundle">Shopify Textmate bundle</a> made development simple and faster than I had anticipated, and the entire launch process was demonstrated clearly through the Shopify interface. </p>
<p>To celebrate our launch we’re offering 20% off all merchandise this week, redeemable by using the discount code <strong>beaverlicious</strong> at checkout. Happy shopping!</p>inga weizmanPartnering with Scalr Gets You a Margarita http://loggly.com/blog/2012/02/working-from-the-beach-with-scalr/2012-02-23T16:12:41-08:002012-02-23T16:10:00-08:00<p><img alt="margarita glass" src="/assets/4f4694b5dabe9d32ba007be3/margarita.png" /></p>
<p>We’ve all dreamed of doing our job while sipping a margarita on the beach, Loggly and <a href="http://scalr.net/">Scalr</a> can get you there...well almost. We love things that scale and automate and all things cloud and that is why we partnered with Scalr. Scalr is an open source cloud management tool that brings automation to web applications. The cloud is all about scalability, growth, and making things easier to manage. Scarl gives you the ability to manage as many servers as you need on different cloud computing services and adjusts load capacity as you have spikes and valleys in your traffic. It’s like a system’s admin machine that never sleeps, doesn’t need energy drinks and coffee, so now your system’s admin can actually can have a life. Loggly comes in and takes care of all the logs, we store them, make them searchable, as well as providing features such as monitoring, troubleshooting and user analytics so nothing is lost and you can figure out what your users are doing. Sit back, relax and see what Scalr can do for you. </p>
<ul>
<li>DNS Management: automatically created and updated for you</li>
<li>Fault tolerance: servers crash, at the worst times, now the problem is detected and automatically resolved for you </li>
<li>Multi-cloud deployments: no need to commit to just one, infrastructure can be spread out across multiple providers </li>
<li>Integrated SSH and key management</li>
</ul>
<p>We're super excited to be partnered with such a cool company that's helping web apps scale and making your life easier. </p>inga weizmanBig Data Uncovered?http://loggly.com/blog/2012/02/a-convenient-truth/2012-02-08T15:14:58-08:002012-02-07T16:00:00-08:00<p class="p1"><span class="s1">I recently came across an eWeek article titled <em><a href="http://mobile.eweek.com/c/a/Cloud-Computing/2012-A-Cloudy-Year-for-Big-Data-102807/">2012: A Cloudy Year for Big Data by Frank Ohlhorst</a></em>. You could easily say I have a few opinions on the matter of big data! :)</span></p>
<p class="p1"><span class="s1">First, I agree with Frank’s first notion that big data is neither big or new. The fact is, I've been saying things like <strong>"Dude, that's a ton of data!"</strong> since I started notching out the opposite sides of floppies back in the 80s. Remember these?</span></p>
<p class="p2"><img alt="" src="/assets/4f32cd57dabe9d5de600a54d/feature_thumbnail/imgres.jpeg" style="width: 240px; height: 203px; " /></p>
<p class="p1"><span class="s1">Ohlhprst quickly follows up his vague handwaving that ‘big data’ a new term with, <strong>“For most of its existence, big data has been out of the reach of small and midsize businesses (SMBs) because the storage and processing power needed to make this technology work is too expensive.” </strong></span></p>
<p class="p1"><span class="s1">Companies have been doing for years what they need to do better business, regardless of whether or not it’s expensive. In manufacturing, the costs of a small company optimizing on how to efficiently making tons of a cheap product can actually be quite a bit more expensive than a larger company making a few units of a complex product. In the same vein, smaller business may have more complex business optimization processes than larger ones, and require relatively larger amounts of data are required to solve those problems than with larger companies.</span></p>
<p class="p1"><span class="s1">I agree with Frank that small business typically don't always have the resources necessary to solve massive scale problems, but again the problems are relative. For example, small software startups don't have project managers where larger ones do, not because they can’t afford them, but because they really don’t need them in a full-time capacity. I think this may be part of why SaaS services have been a huge hit and the term cloud has taken off because of it. SaaS allows companies to tackle a wide variety of problems across the entire business, all the while providing cost effective high tech solutions to solve problems in a way you could never have done before. For the <a href="http://en.wikipedia.org/wiki/Industrial_Revolution">first time in history the quality of business processes is experiencing sustained growth</a>.</span></p>
<blockquote>
<p class="p1"><strong><span class="s1">“These new cloud-based capabilities are on a growth path and are creating more opportunities for even the smallest of businesses to leverage big data without the traditional expenses of compute farms and massive storage arrays.” </span></strong></p></blockquote>
<p class="p1"><span class="s1">Yes. However, compute farms and storage aren't the main thing that these companies need. They need access to the raw data that contains the data about their business, and the tools to extract the data in which they can take action. Figuring out your company’s problems requires brain power, understanding, data and tools. CPU and disks don't solve complex problems. People do.</span></p>
<h3 class="p1"><span class="s1">It's All About Application Analytics</span></h3>
<p class="p1"><span class="s1">Ohlhprst also describes big data analytics as being comprised of three primary elements: volumes of unstructured data, processing power and algorithms. However, big data doesn't always imply unstructured data. Log files, or what Loggly calls <strong>Big Time Data™</strong> typcially contain a large amount of structure. Dealing with structured data isn't always easy, and if you write software that 'expects' a certain structured format, your analysis can sometimes be broken or flawed if it encounters data that doesn't fit the structure you coded for. One way around this problem is to apply extra meta data to the data set. One technique to solving to this problem is adding a search index to the data, which is the approach Google pioniered and what Splunk and Loggly do for log files or time series event data. By being able to do text search data, and interact with it in realtime, or near real time, the user can optimize on solving the problem </span></p>
<p class="p1"><span class="s1">Ohlhorst continues, <strong>“For it to be true big data, there has to be lots of it, and most SMBs don’t generate that volume of data internally, which leads them to seek out alternative data sources. Here, the cloud delivers.”</strong> Not true. Big data should be defined as an amount of data that a human can not reasonably digest. Generating large amounts of meaningful data is actually a bigger problem. Again, it's understanding the problem you have before you can solve it.<br />
<br />
Yup. Ohlhorst explains that throughout 2012, data sets and others can be expected to grow exponentially - <strong>“The amount of data being generated globally increases by 40 percent a year"</strong>, according to the McKinsey Global Institute, a data analytics research firm. True. The access of this data, mostly through the web, <a href="http://www.economist.com/node/15557443">generates vast amounts of data</a> as well. Ohlhorst continues that information needs to be organized, sorted and processed- and that takes computing power. Frankly nowadays, CPU is cheap enough that most of these problems can be solved on your laptop. Fast CPUs for crunching 'big data' aren't the problem any more than a search engine's main problem is crawling for data. The real bottleneck is adding meaning to the data that a customer can digest and make actionable.</span></p>
<h3 class="p1"><span class="s1">PaaS/IaaS Accessibility Is a Problem </span></h3>
<p class="p1"><span class="s1">I’m glad that Ohlhprst recognized that Amazon isn’t the only one in the game in offering private cloud-based big data analytics platforms. He believes that since this technology is designed as a complete platform and not as a service, these platforms are still out of the reach of the SMB market.</span></p>
<p class="p1"><span class="s1">Ohlhorst is right that these platforms are out of reach - but not just because they are designed as a complete platform and not as a service. I think it's because SMBs don't know they need it, don't have the data to put on it, and don't have the resources to manage it. There are plenty of hosted solutions out there (see SalesForce and their app marketplace) that provide some serious horsepower to the most important task - managing a company's contacts.<br />
<br />
And of course, there had to be a <a href="http://splunk.com">Splunk</a> mention in his article. Splunk sells expensive enterprise software. Their software is often times the most expensive piece of software a company has ever bought. Sounds like Oracle, eh? They aren't converting big data analytics into cloud services; they are simply taking their product and making a slimmed down version into a cloud offering they can generate leads with. Any serious big data customer they land will have to buy that very expensive solution and install it on a bank of computers and then pay people to manage it. SaaS is not what Splunk is taking to the market when they go IPO. It's their hellaciously expensive software licenses.<br />
<br />
<strong>Big Time Data™</strong>. It's in the future of your small business.</span></p>Kord CampbellGetting Your Product Stickyhttp://loggly.com/blog/2012/01/getting-sticky/2012-01-16T23:22:31-08:002012-01-13T14:00:00-08:00<p>A few months ago I made an off-the-cuff remark about Loggly. <em>"We're like one of those shitty solar powered calculators. When it gets dark, we forget everything you've typed into us."</em></p>
<p><img alt="" src="/assets/4f10c31ddabe9d16e700a889/feature_thumbnail/loose_calc.jpeg" style="width: 200px; height: 150px; " /></p>
<p>That comment wasn't far off the mark. Historically, we haven't provided a whole hell of a lot of features that makes it easy to jump back into where you left off on your last search session. Basically when you logged out of Loggly, or even closed the shell in your browser, we'd forget everything you searched for until that point. It made it extremely difficult to get back to something meaningful the next time you logged in.</p>
<p>We shouldn't be here if we aren't meaningful. We should deliver users a 'punch in the gut' feature that makes a lasting impact. One they don't want to avoid.</p>
<h3>Saving Time with Sticky Features</h3>
<p>Scaling search for a massive amount of log file data being sent in from thousands of machines has been an overwhelming non-trival problem to solve for us over the past year, and it's been our top priority. Unfortunately us solving scale issues aren't readily obvious to users. Users always expect things on the web to be fast. They could care less how hard a problem it was to solve. They don't think to themselves, <em>"Wow, that's fucking fast!"</em>. <strong> No. </strong> Instead they sit around and mutter things to themselves like <em>"Why the hell doesn't feature X do Y? This thing is wasting my time!"</em>.</p>
<p>And there it is laid bare:<strong> Don't waste your user's time.</strong> It's the most valuable resource they have. Get to the point quick, make it easy to get back to what you were doing next time, and do it all with little fuss and muss.</p>
<p>I say give them sticky features!</p>
<h3>Saved Search and More</h3>
<p>And so, without further ado, I'm officially announcing one of many-to-come new sticky features: <a href="http://wiki.loggly.com/savedsearch?s[]=saved&s[]=search">saved search</a>. Saved search provides users a way to write a search query and then preserve the search to run again later. Saved searches can generate facet graphs or they can simply run a regular search across a given time range.</p>
<p>The shell has been reworked to provide context changes to use with the saved search feature. You can now change the date context, or limit the context to certain inputs, and then rerun the search or graph using the red rerun button at the top.</p>
<p>Here's a quick screencast running through some of our new sticky features:</p>
<p><iframe allowfullscreen="" frameborder="0" height="293" mozallowfullscreen="" src="http://player.vimeo.com/video/35174872?title=0&byline=0&portrait=0" webkitallowfullscreen="" width="520"></iframe></p>
<p> </p>
<h3>Coming Up Soon</h3>
<p>We're continuing to add features that increase stickiness to the product. Next week we'll be releasing a revamped history feature for the shell page, where what you've typed in before in a session will be preserved in your command history, just like it would in a normal shell prompt. We're also adding customized graph selection on the main dashboard, which will allow you to start viewing events that matter most to you by default when you first log in.</p>
<p>All these featuers are leading up to a major revamp of the way we provide value for our user's events. Expect completely customized dashboards for server monitoring, website performance, user analytics, and more soon! If you have a feature you'd like to see us implement, please do drop us a line. We're keen on not wasting your time!</p>Kord CampbellLoggly's Outage for December 19thhttp://loggly.com/blog/2011/12/logglys-outage-for-december-19th/2012-04-30T17:28:38-07:002011-12-19T16:20:00-08:00<p>Sometimes there's just no other way to say "we're down" than just admitting you screwed up and are down. We're in the process of rebuilding the indexes of historic data of our paid customers. This is our largest outage to date, and I'm not at all proud of it.</p>
<h3 class="p3"><b>So What Happened?</b></h3>
<p class="p1">Yesterday afternoon all of our machines on Amazon's East region, availability zone 1d, were rebooted by AWS staff for maintenance purposes. </p>
<p class="p1">The cause of our failure is what some of you on Twitter are calling "a failure to architect for the cloud". I would refine that a bit to say "a failure to architect for a bunch of guys randomly rebooting 100% of your boxes". We've been told by Amazon they actually had to work hard at rebooting a few of our instances, and one scrappy little box actually survived their reboot wrath.</p>
<p class="p1">While some might go on a rant about how 'normal' failures don't affect 100% of your boxes the truth is that any and everything (including an army of reboot monkeys) can be expected to happen to your servers if you wait around long enough. The trick to being good at running a reliable service is to architect around any number of everythings that could happen to your service and build for it.</p>
<p class="p1">In this case we didn't build the workaround simply because the system we run - a combination of 0MQ+Solr+Zookeeper+Loggly Special Sauce - makes it extremely challenging to survive a complete failure with more than 1/2 of the cluster missing. With other challenges facing us, we decided to live with the risk.</p>
<h3 class="p3"><b>So, How Do We Make This Right?</b></h3>
<p class="p1">Single instances of Loggly's search cluster can't be spread across multiple availability zones or regions due to the amount of data we push around, latencies between the search nodes, and the lack of support in our system for redundant indexes. We've been OK with those limitations in the past simple because we systematically archive data to S3 when it arrives and we are capable of rebuilding indexes on the fly if we lose one or more indexers.</p>
<p class="p1">Our primary method to address this will be to start sharding our customers across multiple Loggly deployments. This will prevent further outages to the entire customer base. We've already been investigating other data centers on both dedicated hardware and other cloud-based services.</p>
<p class="p1">Finally, we accept full responsibility for the impact to our customers. We will be in touch with our paid customers sometime over the next week to address compensation for this outage.</p>
<p class="p1">We welcome feedback below.</p>
<p class="p1"><strong>Kord Campbell, CEO</strong></p>Kord CampbellEnabling CORS in Django Pistonhttp://loggly.com/blog/2011/12/enabling-cors-in-django-piston/2011-12-05T15:28:32-08:002011-12-05T15:00:00-08:00<p><img alt="" src="http://blogs.bournemouth.ac.uk/research/files/2011/08/sharing-guinea-pigs.jpg" style="width: 500px; height: 321px; " /></p>
<div>Here at Loggly, one of our goals is to make our API accessible and easy to integrate. By enabling CORS (<strong>Cross Origin Resource Sharing</strong>) on our API endpoints, we hope more Javascript developers can take advantage of what our product has to offer.</div>
<div> </div>
<div>CORS is an addition to the browser security model that allows XHR requests to be made from one domain to another. CORS allows Javascript applications to access resources on domains other than the original document's domain, working around the same-origin policy. While Javascript application developers have crafted techniques like JSONP, Flash proxies, XHR receivers, and server-side proxies to circumvent the same-origin policy, CORS makes these hacks unnecessary.</div>
<div> </div>
<div>To take advantage of CORS both the server and the browser need to support the standard. The browser needs to initiate a negotiation with the server and the server must signal to the browser which domains are allowed to make cross-domain requests. Our current API is implemented in Django Piston, an open-source project that enabled us to quickly build a RESTful API on top of Django. Piston does not support CORS out-of-the-box, but it wasn't hard to write some code to enable it and we'd like to show how it was done.</div>
<div> </div>
<div>A full explanation of CORS is beyond the scope of this post, but the central idea behind CORS is a negotiation between the browser and server of allowed and disallowed actions. This negotiation is done via HTTP headers. The essential headers are the following:</div>
<div> </div>
<ul>
<li><strong>Origin</strong>: Sent by the browser signifying the originating domain.</li>
<li><strong>Access-Control-Allowed-Origin</strong>: Sent by the server, listing the origin domains allowed to make requests to the server's domain. Can be a comma-separated list of domains or "*" to allow requests from all domains.</li>
<li><strong>Access-Control-Allow-Methods</strong>: Sent by the server, listing the HTTP methods the browser is allowed to use in requests to the server.</li>
<li><strong>Access-Control-Allow-Headers</strong>: Sent by the server, listing the HTTP methods the server is willing to accept from the browser.</li>
</ul>
<div>Essentially, to enable CORS we need to have Django Piston respond to an OPTIONS request with the server-sent headers and send the requisite headers along with responses.</div>
<div> </div>
<div>The <strong>Resource</strong> class is the heart of a Django Piston-built API. The code that injects the headers into responses lives in a subclass of the base Resource class. We've called this class <strong>CORSResource</strong>:</div>
<div> </div>
<script src="https://gist.github.com/1429054.js?file=cors_resource.py"></script>
<div> </div>
<div>The <strong>CORSResource</strong> performs two simple tasks. First, it intercepts any OPTIONS method requests to handle the pre-flight negotiation between the browser and the server. Since OPTIONS requests do not have a response body, an empty <strong>HTTPResponse()</strong> is returned along with the requisite headers. Second, <strong>CORSResource</strong> intercepts responses from the Django Piston handlers (where responses are generated) and decorates them with the CORS headers.</div>
<div> </div>
<div>To use <strong>CORSResource</strong>, we simply instantiated our endpoints with the <strong>CORSResource</strong> sub-class instead of the base <strong>Resource</strong> class. The change to our API's <strong>urls.py</strong> file look like this:</div>
<div> </div>
<script src="https://gist.github.com/1429077.js?file=urls.py"></script>
<div> </div>
<div>We hope this post helps other Django Piston API implementors enable CORS in their own APIs. We're planning to release this implementation in the coming weeks and we're looking forward to see what Javascript developers are going to do with direct access to our API.</div>
<div> </div>
<div>Happy hacking!</div>
<div> </div>
<div>(image from http://blogs.bournemouth.ac.uk/research/2011/09/01/sharing-your-research-data/)</div>
Ivan TamJava Code Coverage With Cobertura and Jenkinshttp://loggly.com/blog/2011/11/java-code-coverage-with-cobertura-and-jenkins/2011-11-29T14:44:13-08:002011-11-29T14:00:00-08:00<h2>Introduction</h2>
<div> </div>
<div>Writing software is tough, tools and processes that reduce the difficulty are always welcome. Code Coverage is an interesting developer tool because it tells you how much you don't know. Not having a test for a particular piece of code doesn't mean that it doesn't work, it just means you aren't as sure as you could be that it works. In this article, I will be covering getting the open source, Java Code Coverage tool, Cobertura, working with Ant, Jenkins and Github. </div>
<div> </div>
<h2>Cobertura</h2>
<div> </div>
<div>Since getting continuous integration working in a particular language can be complicated, it is a best practice to break the problem down into discreet chunks. Fortunately, Cobertura makes this easy, because the source code comes with an example ant project. You can download a binary distribution here: <a href="http://cobertura.sourceforge.net/download.html">http://cobertura.sourceforge.net/download.html</a>. Inside of the download will be a relative path <em><strong>..examples/basic</strong></em>. If you cd into that directory, you can generate a code coverage report on a sample Java project by typing: </div>
<div> </div>
<div><em><strong>ant </strong></em></div>
<div> </div>
<div>If you don't have junit installed, you will get output like the below, on my OS X Lion laptop: <script src="https://gist.github.com/1398307.js?file=CoberturaNoAnt.txt"></script></div>
<div> </div>
<div> </div>
<div>If you run ant clean it will remove the reports directory, which contains, old reports. One tricky bit with running the example, is that the build.xml file is set to look for cobertura, and the cobertura lib directory, in a relative path two directories above. <script src="https://gist.github.com/1405656.js?file=build.xml"></script></div>
<div> </div>
<div> </div>
<div> </div>
<div>This can be tricky if you check in only the examples directory into your git repository and don't have cobertura installed properly. What will happen then, is that the examples won't build properly and it will be tricky to figure out. If you find yourself in this situation, one easy hack is to simply place the cobertura jar files, including the dependencies in the lib, inside of your ant home. On Ubuntu linux this is <em><strong>/usr/share/ant/lib</strong></em>.</div>
<div> </div>
<h2>Jenkins</h2>
<div>With the basic Cobertura configuration out of the way, the next thing to do is to create a throwaway git repository, on github or your own server. Next you will want to check in the whole Cobertura binary distribution that you download from their site: <a href="http://cobertura.sourceforge.net/">http://cobertura.sourceforge.net/</a>, or only check in the examples root, and install Cobertura properly on your build server. Inside of jenkins you will need to do the following things:</div>
<div> </div>
<div>1. Point Jenkins at your git repository.</div>
<div>2. Install the Cobertura jenkins plugin: <a href="https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin">https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin</a></div>
<div>3. Configure the reports directory correctly. Because I only checked in the examples directory, my reports configuration looked like this:</div>
<div> </div>
<div><em><strong>**/reports/cobertura-xml/coverage.xml</strong></em></div>
<div> </div>
<div>4. build it.</div>
<div> </div>
<div>Here is a screenshot of what it looks like when Jenkins builds the example project with code coverage:</div>
<div> </div>
<div><img alt="" src="/assets/4ed54eabdabe9d4e5b0191f4/feature_zoom/jenkinscodecoverage.png" /></div>
<div> </div>
<h2>Conclusion</h2>
<div>If you were able to follow along at home, and have Jenkins automatically building the example project with code coverage, then the next step is to convert this knowledge to your own code base. One problem I had when getting Cobertura working with my code base was configuring the junit stanza properly to fork. Finally, an even simpler way to get code coverage cooking for your Java code base is to install the Eclipse plugin eCobertura. If you are really stuck getting things to work, this is a nice easy win. I hope you enjoyed the article, and if you have any Cobertura tricks, I would love to hear about them.</div>
<div> </div>
<div> </div>
<div>Ant: <a href="http://ant.apache.org/">http://ant.apache.org/</a></div>
<div>Cobertura Plugin Jenkins: <a href="https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin">https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin</a></div>
<div>Jenkins: <a href="http://jenkins-ci.org/">http://jenkins-ci.org/</a></div>
<div>Github: <a href="https://github.com/">https://github.com/</a></div>
<div>Cobertura: <a href="http://cobertura.sourceforge.net/">http://cobertura.sourceforge.net/</a></div>
<div>Measuring Code Coverage With Cobertura: <a href="http://www.ibm.com/developerworks/java/library/j-cobertura/">http://www.ibm.com/developerworks/java/library/j-cobertura/</a></div>
<div>eCobertura: <a href="http://ecobertura.johoop.de/">http://ecobertura.johoop.de/</a></div>
<div> </div>
<div> </div>
Noah GiftAutomated Integrationhttp://loggly.com/blog/2011/11/automated-integration/2011-11-18T16:55:15-08:002011-11-16T09:45:00-08:00<p>One of the fundamental challenges of distributed coding is deciding what/when to integrate. Sure, that patch your colleague just sent you looks good, but is it actually ready to go into master? At Loggly, we've been feeling our way towards a disciplined integration process. A year ago, our frontend developers were all making commits directly to trunk in a single SVN repo. Once every few weeks, we'd run `svn up` on our servers, and hope for the best. Today our code goes through peer review, unit testing, and static analysis before it even touches our master branch.</p>
<p>Like most projects these days, the process starts on <a href="https://github.com/">github</a>. Fork. Push a feature branch to your repo. Open a pull request. Go through a couple rounds of discussion and revision. Merge. Every change to our code goes through this process. At first we thought it would slow us down, that we'd want pull requests for the nontrivial code and to just push to master for the easy stuff. After just a few days, we found the pull requests were slowing us down not at all, and that we all enjoyed the greater transparency into our colleagues' work.</p>
<p>Once we merge, the automation kicks in -- our default integration branch is 'proposed', so clicking merge doesn't actually get the code into the master branch. Jenkins polls our 'proposed' branch once a minute, then runs a simple preflight script on the code.</p>
<p>Rather than keep that preflight in a jenkins configuration page, we have it checked into the codebase so that any developer can run it too; this way there's no excuse for breaking the build -- you should have seen it break locally =P</p><script src="https://gist.github.com/1368420.js?file=gistfile1.txt"></script>
<p>Here's our preflight script. Let's go through it line by line.</p>
<p>DIR="$( cd "$( dirname "$0" )" && pwd )"</p>
<p>APP=$DIR/..</p>
<p>First we figure out where we're running, so that we can find the other scripts distributed with the app. </p>
<p>$DIR/purge_pyc</p>
<p>Next we purge pyc files. This is done because if a user recently switched from a branch which contained files which don't exist in our branch, the pyc files may still be around, and may be found by the interpreter. </p>
<p>$DIR/syncenv</p>
<p>Next we run a script to sync our <a href="http://www.virtualenv.org/en/latest/index.html">python virtual environment</a>, and ensure all requirements are present. </p>
<p>$DIR/runtests</p>
<p>Here, of course, we run our unit tests. Each run prints a coverage report, so that as we recover from our testing debt, we can measure our progress.</p>
<p>&& $DIR/lint...</p>
<p>Next, and this is important, we run <a href="http://www.logilab.org/card/pylint_manual">pylint </a>over the parts of our app that we expect to pass with no warnings. As we clean up our app, we continue to add modules to this list. Pylint does a few useful things for us. It looks for trivial name errors of the kind that could quickly cause code to stacktrace -- using a module without importing it, etc. It also enforces certain kinds of coding discipline. Our functions and modules can't exceed a certain length. The <a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity">cyclomatic complexity</a> of our functions is limited. </p>
<p>If all of this passes successfully, Jenkins automatically pushes the checked-out commit to master, which is where we base our development. Thus, we're always basing our development on known-vetted code.</p>
<p>If any of it fails, Jenkins still has a couple more tricks to pull. Here's our on-failure script:</p><script src="https://gist.github.com/1368451.js?file=Loggly%20Failure%20Script"></script>
<p>This comes in two parts. The first runs a standard-issue git-bisect between origin/proposed and origin/master. Since origin/master has already been vetted by jenkins (that's how it became master), we know there'll be a regression somewhere between the commits. This goes into the session output, and is e-mailed to the relevant committers. Next, we roll the proposed branch back to the already-vetted master branch. Whatever pull request broke the build will have to be re-made from scratch.</p>
<p> </p>Mike BlumeI'm Presenting at MongoSV!http://loggly.com/blog/2011/11/im-presenting-at-mongosv-1/2011-11-10T14:36:50-08:002011-11-10T14:00:00-08:00<p>In May I had the opportunity to <a href="http://loggly.com/blog/2011/07/visualizing-your-data-with-mongo/">speak at the MongoDB conference in SF</a>. I ran through how Loggly uses MongoDB to store statistical data on the data we receive and how customers can use our API calls to render visualizations of their logging data. I also hacked a visualization example of events per device using the <a href="http://mbostock.github.com/protovis/">smashing protovis.js</a> library, which you can <a href="http://house.geekceo.com:8082/">view here</a>.</p>
<p><img alt="" src="/assets/4ebc4b57dabe9d2b55005839/feature_thumbnail/mongo_viz.png" /></p>
<p>I’m thorougly excited to announce I will be speaking again at the <a href="http://www.10gen.com/events/mongosv-2011">MongoSV conference in Santa Clara in December</a>. I'm stepping up the presentation this time, and will be showing more details about how we've integrated MongoDB into our infrastructure, and how we use MongoDB to generate our <a href="http://en.wikipedia.org/wiki/OLAP_cube#OLAP_operations">roll up reports</a>. I'll run through how we use these roll up stats to drive our own metrics dashboard and how exposing the data to end users via our REST APIs provides a whole host of cool options for visualizing data. </p>
<p>If you’re in town or live within a reasonable distance, defined as the entire Contentintal United States, be sure <a href="http://www.10gen.com/events/mongosv-2011">to come check out the conference</a>! The <a href="http://www.10gen.com/">Mongo/10gen crew</a> always puts on great events filled with lots of technical talks and a rocking after-party. </p>
<p>See you there! Again.</p>Kord CampbellAjax This!http://loggly.com/blog/2011/11/ajax-this/2011-11-08T14:46:46-08:002011-11-07T09:00:00-08:00<p>Today’s blog post is more of a tool than a toy. Lately I’ve been working on a bookmarklet that utilizes <a href="http://www.pusher.com" target="_blank">Pusher</a> (if you want to learn a little about Pusher, <a href="http://loggly.harmonyapp.com/blog/2011/10/get-real-with-pusher/" target="_blank">check out my previous blogpost</a>). The bookmarklet I’m working on is really silly, but these technologies have the potential to be used for some really cool apps.</p>
<p>I needed to figure out a way for javascript to manipulate objects on web pages remotely through <a href="http://en.wikipedia.org/wiki/Ajax_(programming)">ajax</a> calls. You can already do this with click events and Pusher by sending the class or ID of any element you click on, but what if there is no class or ID? I also want this to work on any webpage. As a result, I needed to figure out how to build a selector that was as unique as possible. One way to do this is to select using all attributes the object has to offer.</p>
<p>Enter, the custom attribute selector syntax: <script src="https://gist.github.com/1339876.js"> </script></p>
<p>This selector will only select objects that have a class value of “whatever” AND a myattribute value of “customattribute”.</p>
<p>Some stack overflow and basic google searching told me I can tease attributes out using a regex(gross). I knew there had to be a better way so I started fiddling with javascript objects I captured on click and tried to figure out how to uniqify my selector string. What I discovered is that every object I clicked on contained a ton of info. The parts that I use to build my unique-ish selector are:</p>
<ul>
<li>localName</li>
</ul>
<p>The name of the html tag. in the gist above, this value is customhtml.</p>
<ul>
<li>attribute</li>
</ul>
<p><span>All attribute(s) associated with the object (if any). In the gist above, these attributes are class=”whatever” and myattribute=”customattribute” </span></p>
<ul>
<li>parentNode</li>
</ul>
<p>This is the object that contains the one clicked on. Technically my gist has no parent, but I use the parent node to map out a path from the object I clicked on all the way up to the body of the page. If the object you click on doesn’t have any attributes ( a <p> object for example) you can still select it based on the specific path to that object.</p>
<ul>
<li>textContent</li>
</ul>
<p>This is any text that’s in the object. For example <p>writing stuff</p> will have a textContent value of “writing stuff”.</p>
<p>Apart these elements aren’t bad at selecting the object you want, but together they get pretty close to behaving like a this object. </p>
<p> </p>
<p>Here is how I stitch these elements together to create my this-ish selector:</p>
<script src="https://gist.github.com/1339965.js"> </script>
<p>There are a few tricks here:</p>
<ul>
<li>I found that if the object you click on has no attributes the path doesn’t help too much, so if the object I’m building a selector for doesn’t have any attributes I just give it a class with an empty value, and it works really well. </li>
<li>In an attempt to keep my selector as unique as possible I’m also using :contains() to select on specific strings as well as the path. This helps ensure that you’re manipulating the exact object you want.</li>
<li>If you’re clicking on an image tag, I found that contains won’t allow you to select that image, but if we take it out it works, so I added a filter to keep image tag selectors from using :contains(). (although it currently only works in the developer tools javascript console, I haven’t yet figured out why it’s not working for my bookmarklet).</li>
</ul>
<p>Generally, the resulting selector ends up looking something like this:</p> <script src="https://gist.github.com/1340021.js">
<p>The very last part of making all of this work happens on the receiving end. If you are sending this selector string (or multiple selector strings) as a JSON encoded object, the string you get back has all the quotes escaped. In this case to turn your string into a valid selector all you have to do is the following:</p>
<script src="https://gist.github.com/1340068.js"> </script>
<p>While this method doesn’t currently work on every single element of every single webpage, it has worked on most of the pages I have tried out, including the google search page.</p>
<p>Anyone who has every tried to explain, on the phone (so 1991), to a grandparent or non-tech savvy friend/family member how to do certain things on the web should be able to see the power of this. With these selectors it’s now possible to build a bookmarklet that allows two or more people to see what elements are being clicking on. Now describing webpage elements to your grandfather isn’t needed. Hook him up with your Pusher powered bookmarklet and all he has to do is go to a URL, click a bookmark button, and let’s say... click on the text you just highlighted in yellow. On the flip side, you’ll know (almost)exactly what elements on the page he’s clicking on because his clicks will make highlights on the page as well. Super neat!</p>
<p>Obviously this method isn’t perfected, but if you’re interested helping me make it more precise, <em>please</em> fork my project on github.</p>
<p><a href="https://github.com/brainTrain/ajaxThis">https://github.com/brainTrain/ajaxThis</a></p>
<p> </p>Brian SchroederPagerDuty, Loggly, and Alert Birdshttp://loggly.com/blog/2011/10/pagerduty-loggly-and-alert-birds/2011-10-28T16:48:52-07:002011-10-28T14:45:00-07:00<p>Well, I'm back, and this time I'm here to talk about an awesome product that we use all the time, <a href="http://www.pagerduty.com/">PagerDuty</a>. We use it internally for our own alerting (as do a number of Fortune 500 companies along with a million other startups), but we've also integrated it into <a href="https://alertbirds.appspot.com/">Alert Birds</a>, which is our alerting tool. With Alert Birds, you can configure saved searches that run against Loggly, and you'll run those searches over a period of time that you've selected, and Alert Birds will escalate alerts in PagerDuty. Before you can do any of those things, however, you need to set up the PagerDuty endpoint:</p>
<p><img alt="" src="http://wiki.loggly.com/_media/pd_ep.png" style="width: 331px; height: 502px; " /></p>
<p> </p>
<p>After you've done that, the next thing you'll need to do is to configure a saved search, and then configure the alert that you want to run. The search itself is pretty straightforward, it has a name, a search string e.g.</p>
<p><script src="http://gist.github.com/1323693.js"> </script></p>
<p>(this is why it's cool to send us JSON!), and a list of inputs and devices that you choose - you may want to run a particular search on only your web servers, for instance. The interesting bit is the alert itself, which runs a search that you choose, but has a number of options as to what conditions consitute an alert, and what the message should be:</p>
<p><span id="cke_bm_388E" style="display: none; "> </span><span id="cke_bm_387E" style="display: none; "> </span></p>
<p><img alt="" src="http://wiki.loggly.com/_media/ab_ca.png" style="width: 573px; height: 543px; " /></p>
<p> </p>
<p>This is where PagerDuty comes in. Although you can send a GET or POST request to an endpoint of your choosing with the alert data, triggering an alert in PagerDuty is far more useful, as they can SMS/email/phone you, and they also handle escalations and reporting. So, in the example above, if my web servers are spewing 500 exceptions, I want my ops folks to get notified, provided there are more than 10 - I don't want to wake anyone up over a little blip! I'm a nice IT manager like that. Anyhow, once an alert is in a critical state, it will run your search every minute until you're below the threshold, and once that happens, Alert Birds will automatically resolve your alert in PagerDuty.</p>
<p>That's pretty much all there is to it! You can find the docs on Alert Birds <a href="http://wiki.loggly.com/alertbirds">here</a>, please do drop me a line at support@loggly.com if you need a hand, and until next time, happy alerting!</p>David LansteinAlert Birds, OAuth, and Logglyhttp://loggly.com/blog/2011/10/alert-birds-oauth-and-loggly/2011-10-10T17:15:15-07:002011-10-10T17:14:00-07:00<p>In today's edition of the Loggly Blog, we're going to talk about integrating with Loggly using OAuth. You can authenticate against Loggly in a couple different ways, but we're going to focus on OAuth today, because that's how Alert Birds is able to run saved searches on a user's behalf. OAuth can be a little tricky to set up at first, which is a big reason why we're open-sourced Alert Birds, and the code can be found at <a href="https://github.com/loggly/alertbirds-community-edition">http://github.com/loggly/alertbirds-community-edition</a>. </p>
<p>To build an app, the very first thing you'll need to do is to register an app in your Loggly instance, at /account/applications/. Apps are called 'Consumers' in OAuth parlance, and after your application gets approved, you can get started working with our API using OAuth.</p>
<p>In basic terms, the OAuth flow looks something like this:</p>
<p>1. Get a request token, which is good for a single request</p>
<p>2. Hit the authorization URL, and pass the request token</p>
<p>3. Log into Loggly when redirected</p>
<p>4. Authorize the application with your consumer key to access Loggly on your behalf</p>
<p>5. Get redirected back to your app, and with the OAuth verifier and the request token, get an access token, which is what you'll need to make future requests</p>
<p>This sounds pretty simple, but everything has to be just right for the flow to work. Query string parameters have to be encoded in the right order, you need to send the correct HTTP method that Loggly is expecting, and you can't lose the OAuth verifier or the request token, because if the access token request fails, you'll have to ask the user to re-authenticate your app - no good! </p>
<p>The OAuth wrapper you'll probably want to use is here: <a href="https://github.com/loggly/alertbirds-community-edition/blob/master/lib/oauth.py">https://github.com/loggly/alertbirds-community-edition/blob/master/lib/oauth.py</a>, and a working client code implementation is here: <a href="http://github.com/loggly/alertbirds-community-edition/blob/master/controllers/main.py">http://github.com/loggly/alertbirds-community-edition/blob/master/controllers/main.py</a>. The wrapper class simply wraps the <a href="https://github.com/dgouldin/python-oauth2">OAuth 2 Python library</a>, and makes it a little easier to interact with Loggly, and the client code is where you'll find the interesting stuff in terms of hanging onto the request token, handling the return from Loggly, etc. </p>
<p>A simplified walkthough of what's happening in main.py is this:</p><script src="https://gist.github.com/1276890.js"> </script>
<p>That's pretty much all there is to it. Using the lib/oauth.py wrapper from Alert Birds should make it considerably easier to get going. Happy coding!</p>David LansteinGet Real with Pusher! Getting Those Alert Birds Squawking...http://loggly.com/blog/2011/10/get-real-with-pusher/2011-10-04T21:43:50-07:002011-10-04T09:42:00-07:00<p><img alt="" src="http://loggly.com/assets/4e85f424dabe9d5963015388/screen_shot_20110930_at_9.45.18_am.png" /></p>
<div> </div>
<div><strong><a href="http://pusher.com/" target="_blank">Pusher</a> rocks!</strong> It gets real with the web in a major way. Websockets! I'm the new kid on the Loggly block and I've been working on Loggly's first <a href="http://loggly.com/features/systems-monitoring-allerting/">alerting</a> app, <a href="http://www.alertbirds.com" target="_blank">Alert Birds</a>. Alert Birds uses Pusher because it's fast (real time is kinda hard to beat) and it's easy to use. </div>
<div> </div>
<div>I'm giving you guys some code to play around with. Using Pusher and <a href="http://www.schillmania.com/projects/soundmanager2/" target="_blank">Sound Manager</a>, we're going to set up a site that makes Loggly's Alert Birds hop and sing on a page whenever anyone clicks on them. That means you see when your buddies click birds, and vise verse. Just think of it as a bird chatroom. Trust me, it's awesome.</div>
<div> </div>
<div><strong>If you want to follow along with the code, clone the github repo I created here:</strong></div>
<div><a href="http://github.com/brainTrain/PusherBirds" target="_blank">http://github.com/brainTrain/PusherBirds</a></div>
<div> </div>
<div>All you need to do in order to get pusher working is sign up for an account, grab some credentials and get coding.</div>
<div> </div>
<div><strong>Pusher has two main components:</strong></div>
<ul>
<li> Listening for events.</li>
<li> Triggering events.</li>
</ul>
<div> </div>
<div><strong>Let's start with listening:</strong></div>
<div> </div>
<div>To listen for a pusher event you only need the app key, channel name and event name. With these compoenets listening to a Pusher channel is pretty straight forward and looks something like this:</div>
<script src="https://gist.github.com/1262329.js?file=pb_1"></script>
<div>
<pre style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; ">
</pre>
<div class="line" id="LC1" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 1em; line-height: 1.4em; "> </div>
</div>
<div> </div>
<div><strong>Triggering </strong>is just as straight forward and requires the same elements as listening, plus the app secret and a backend to handle and send data to Pusher.</div>
<div> </div>
<div>First you'll need some data to send, let's go nuts with ajax!</div>
<script src="https://gist.github.com/1262362.js?file=pb_2"></script>
<div> </div>
<div>This brings us to the backend component of triggering events, so let's look at the trigger.php file.</div>
<script src="https://gist.github.com/1262375.js?file=gistfile1.aw"></script>
<div> </div>
<div> </div>
<div>The first thing you need to do is head over to pusher's Publisher Libraries and choose the backend language you heart the most.</div>
<div> </div>
<div><strong>You can find Pusher's list of Publisher Libraries here</strong>: <a href="http://pusher.com/docs/rest_libraries" target="_blank">http://pusher.com/docs/rest_libraries</a></div>
<div> </div>
<div>Once you do that simply follow the Library authors instructions and go nuts! In my case I chose the generic php library to demonstrate this simple example. It's all fairly straight forward but I did run into a namespace issue with my host. To fix it I simply commented out the namespace in Pusher.php.</div>
<div>
<div> </div>
<div>In trigger.php I add Pusher.php using php's require function, put my keys, secrets etc into variables and use Pusher.php's Pusher() class to validate. The last line triggers the event and grabs data from the button variable I sent to the URL using my ajax call.</div>
<div> </div>
<div>I should point out that Pusher is somewhat flash dependent as well. For browsers that don't support websockets, they have a solution. Flash. Which means it's probably a good idea to handle flashblockers for any pusher app you create. </div>
<div> </div>
<div><strong>Here's Pusher's list of supported browsers: </strong> <a href="http://pusher.com/docs/browser_compatibility" target="_blank">http://pusher.com/docs/browser_compatibility</a></div>
<div> </div>
<div><strong>That's it!</strong></div>
<div> </div>
<div>At this point you should have everything you need to communicate with our Alert Birds. Go nuts!</div>
<div> </div>
</div>Brian SchroederCreating Heroku Addons: Getting Startedhttp://loggly.com/blog/2011/09/creating-heroku-addons-getting-started/2011-09-27T16:46:08-07:002011-09-27T09:49:00-07:00<p><img alt="Heroku" class="prestyled" src="http://www.wemakeprojects.com/files/2011/06/heroku-logo_big-300x93.png" style="width: 300px; height: 93px; " /></p>
<div>Recently the Loggly add-on went live for all Heroku users. Creating this add-on involved implementing a few API endpoints in our service for Heroku to call. This is a quick overview of what the development process looks like.</div>
<div> </div>
<div>Heroku's add-on API specs have a number of calls that fall into the following three categories:</div>
<ul>
<li><strong>Provisioning/deprovisioning</strong>: These are calls that ask the add-on service to create or remove resources on behalf of the Heroku user. One thing to remember here is that Heroku will ask the add-on service to create resources per Heroku *app*, not per Heroku user account.<br />
In most cases, when Heroku makes a provisioning call to the add-on service, a new account is created for a Heroku app.</li>
<li><strong>Single-sign-on</strong>: Heroku users do not need to provide separate account credentials to access services the add-on provides. As long as they are logged into Heroku, they are able to use or configure the add-on. This is accomplished by forwarding the user to the add-on's URL. A token and a timestamp are forwarded along with this request for authentication.</li>
<li><strong>Plan change</strong>: Heroku allows add-on providers to change users based on tiers of service. A Heroku user is free to upgrade or downgrade add-on tiers at any time.</li>
</ul>
<div> </div>
<div>Each of these required actions corresponds to a REST endpoint that the add-on service</div>
<div>must implement:</div>
<ul>
<li><strong>POST</strong> and <strong>DELETE</strong> to <strong>http://<addon_service>/heroku/resources</strong> respectively provision and deprovision accounts.</li>
<li><strong>GET</strong> to <strong>http://<addon_service>/heroku/resources/<id>?token=<token>&timestamp=<timestamp></strong> does authentication and any necessary forwarding for SSO.</li>
<li><strong>UPDATE</strong> to <strong>http://<addon_service>/heroku/resources/<id></strong> will update the account's service tier.</li>
</ul>
<div> </div>
<div>Heroku provides an awesome tool called <strong>kensa</strong> that simulates API calls from Heroku on these endpoints to test the add-on service. </div>
<div> </div>
<div>To install <strong>kensa</strong> open up a terminal and make sure you have Ruby and gems installed and type:<br />
</div>
<pre style="margin-left: 40px; ">
sudo gem install kensa
</pre>
<div><strong>Kensa</strong> is used to generate the boilerplate manifest that describes properties of the add-on:</div>
<pre style="margin-left: 40px; ">
Brasero:blogpost ivan$ kensa init</pre>
<pre style="margin-left: 40px; ">
Initialized new addon manifest in addon-manifest.json
Brasero:blogpost ivan$ cat addon-manifest.json
{
"id": "myaddon",
"api": {
"config_vars": [ "MYADDON_URL" ],
"password": "QQMmzfm3pkzzE3Fn",
"sso_salt": "eqs5xOh7kGqen5hd",
"production": "https://yourapp.com/",
"test": "http://localhost:4567/"
}
}
</pre>
<div>Edit the <strong>id</strong> to name the add-on. Edit the <strong>test</strong> and <strong>production</strong> urls to reflect the hostnames of your respective test and development hosts.</div>
<div> </div>
<div>Heroku doesn't call it as such, but <strong>kensa</strong> is also a tool that cleverly puts test-driven development into your add-on development process. When you run the tool in test mode, it makes REST calls to the host defined in the <strong>test</strong> directive of the <strong>addon-manifest.json</strong> file:<br />
</div>
<pre style="margin-left: 40px; ">
Brasero:workspace ivan$ kensa test provision
Testing manifest id key
Check if exists [PASS]
Check is a string [PASS]
Check is not blank [PASS]
Testing manifest api key
Check if exists [PASS]
Check is a hash [PASS]
Check contains password [PASS]
Check contains test url [PASS]
Check contains production url [PASS]
Check production url uses SSL [PASS]
Check contains config_vars array [PASS]
Check containst at least one config var [PASS]
Check all config vars are uppercase strings [PASS]
Check all config vars are prefixed with the addon id [PASS]
Check deprecated fields [PASS]
done.
Testing POST /heroku/resources
Check response [FAIL]
! expected 200, got 401</pre>
<div> </div>
<div>The output above is typical from early runs of <strong>kensa</strong>, but by running the kensa tool early and often the test failures will guide development.</div>
<div> </div>
<div>After all the provision tests pass, invoke the next set of tests for deprovisioning by running:<br />
</div>
<pre style="margin-left: 40px; ">
Brasero:workspace ivan$ kensa test deprovision <some account id>
</pre>
<div>Follow through with the <strong>planchange</strong>, and <strong>sso</strong> tests. When all tests pass, the integration is complete!</div>
<div> </div>
<div>What remains is to upload <strong>addon-manifest.json</strong> to your Heroku provider account and to document the add-on for the Heroku Addon Catalog. More information on how to do that is available <a href="https://addons.heroku.com/provider/resources/technical/build/submit">here</a>.</div>
<div> </div>
<div><strong>Other tips:</strong></div>
<ul>
<li>Until the add-on leaves Beta, the only plan available to test is the <strong>test</strong> plan. Be sure your add-on has a <strong>test</strong> plan during development.</li>
<li>Don't go into Beta until the add-on is feature complete. An add-on cannot be taken out of Beta back into Alpha without help from Heroku.</li>
</ul>
<div> </div>
Ivan Tam