Tuesday, 21 October 2014

Why fake RPM's ?

In IT companies across the globe, infrastructure and servers are provisioned at the click of a button - this is the world the Devops engineer is creating.  The deployment tool we used at this particular organisation is called ansible.  Ansible allows the programmatic install of RPM's and config files on target servers - our whole estate was built off the back of ansible scripts.

We wanted to develop the ansible scripts using TDD (Test Driven Development), so that we could put the whole regression test and deployment into a pipeline.

Importantly for TDD to work you get yourself into a feedback loop:

1) Write specs
2) Update ansible scripts
3) Run specs  (this will start with an empty base server build then apply the ansible scripts)
4) Repeat 2 and 3 until specs pass

The problem we had was that running the specs could take up an hour.  The feedback loop was way too long!  It turned out that some of the RPM's which the ansible scripts install took the best part of 30 mins to complete.

So we made a compromise - we would install fake rpms for the longest running offenders (luckily these were "bloated 3rd party monitoring tools" so not critical to the servers functionality)

FPM

FPM is a ruby gem to create .rpm's (or .debs if running on ubuntu).  To install you will need ruby:

curl -sSL https://get.rvm.io | bash -s stable
rvm install 1.9.3
rvm use 1.9.3 --default
gem install fpm


Fake it

The idea, is to add the fake rpm to the yum repository (alongside the original), but give the fake a higher version number, so it gets picked up by the yum install command.

What we found was that in some of the in house rpm's, the epoch had been set to the unix epoch time and in fact whenever the rpm was regenerated the epoch would increase.  In this scenario, the highest epoch wins, irrespective of the version number.

So the strategy for creating the winning the version, is very much dependent on how the original RPM was versioned:
mkdir -p /tmp/fake_package
fpm -s dir -t rpm -n sysmontool -v 999999999 --epoch 2000000000 -f -a noarch -C /tmp/fake_package ./
fpm -s dir -t rpm -n patchmontool -v 0.0.1 --epoch 2000000000 --iteration 999 -f -C /tmp/fake_package ./


Strangely, you can not just set the epoch to 9999999999,   We actually set the epoch to `date +%s`

Conclusion

The fpm command will create a .rpm.  Copy this into your yum repository and do a yum update.  You make need to do a yum clean all if its not getting picked up.

From experience, for server builds which are automated, it might be a good idea to set the yum,conf metadata_expire field to a low value.  This avoids the confusing scenario of thinking that something has installed/updated when it hasn't.



Wednesday, 15 October 2014

How to pair using TMUX

Why?

Tmux provides a way to remotely pair using a shared terminal.  This is great as it means you can pair when working from another office or from home.  It's ideal if you are comfortable using a command line, and enjoy memorizing keyboard shortcuts.

Issues


Re-sizing windows - when one side of the pair has a different resolution or differently sized terminal window, if you're not careful, you will see screen sheering.  This was completely solved by using putty or cygwin and ensuring that both clients use the same font settings.  Once its working, it works really well:





Weird control characters - If you see weird characters on the screen then set the window translation -> character set translation to UTF-8.  This seems to be only a problem in putty.

Git bash - Tmux does not work nicely with git bash

Server Setup

Assuming you are using vagrant on your local PC, create a vagrant server on 192.168.33.10, using the following vagrant snippet when setting up the network:

config.vm.define "a" do |server|
  ## usual config
  ## ...
  server.vm.network "private_network", ip: "192.168.33.10"
  server.vm.network "forwarded_port", guest: 2222, host: 22
end

What's important here is that you forward 2222 to the ssh port 22.

ssh onto your vagrant instance and install and run tmux:
     
yum install -y tmux
tmux

Now use the ip of your physical box to ssh in:

Client Setup

Install cygwin or putty.  From cygwin remotely attach to the tmux session:

ssh vagrant@<ip>  
tmux attach  (this will attach to the session started above)

Remote Connectivity

  • Use a wired connection.  Once I plugged in directly to the home router a lot of the connectivity issues went away (despite having a 5 bar wireless connection).
  • If using vmware or virtual box to host vagrant instance, ensure you use Bridged Mode.  If you use NAT mode, we found the connection would occasionally drop.  
  • If you have both vmware and virtual box running, then it may not be possible to enable bridged mode.  Uninstall one of them!


Viber

We tried several voip clients, including skype and googlr hangouts... The most stable turned out to be Viber.  I installed viber as a client on the remote PC.  Combined with a wired connection, it made the voip experience rock solid.
  • Use viber with a hands free headset.  I used a cheap bluetooth headset from amazon.
  • Be warned that voip eats bandwidth - try and get a wifi connection.


Conclusion

Tmux takes a bit of getting used to - you may find the default bind key is a bit of a stretch (ctrl-b).  I changed it to (ctrl-a).  Use this Tmux cheat sheet to quickly get up and running.  I've put together a set of dotfiles to make the whole tmux experience a lot nicer: https://github.com/coder36/dotfiles



Back button bindings

Sometimes you need to change what happens when the browser back button is pressed - for example you might want to disable the back button, or as I did, bind it to a hidden button.

The simplest way is as follows (assuming you are using jquery):

Javascript:

$( function() {
  history.pushState("back", null, null);
  if (typeof history.pushState === "function") {
    history.pushState("back", null, null);
    window.onpopstate = function (evt)
    {
      history.pushState('back', null, null);
      alert( "Back button pressed!" )
     };
   } 
})


Coffeescript:

$( () ->
  history.pushState("back", null, null);
  if typeof history.pushState == "function"
    history.pushState("back", null, null);
    window.onpopstate = (evt) ->
      history.pushState('back', null, null);
      $( "#back_a_page" ).click()
)


The coffeescript example will result in the button with id="back_a_page" getting clicked, whenever the back button is pressed.  

In chrome, to quickly test, this, go to  github  (which uses jquery) and hit the F12 button, then select console.  Paste in the javascript code and hit enter.  Now hit the back button :) 

Thursday, 10 October 2013

Ubuntu - restarting your network

Don't forget that when you do this over an ssh/putty session then your connection will be dropped:

sudo ifdown -a
sudo ifup -a
sudo /etc/init.d/networking restart

Monday, 24 June 2013

Wordchain puzzle

Word Chains


Taken from http://codekata.pragprog.com/2007/01/kata_nineteen_w.html

Write a program that solves word-chain puzzles:

Find a chain of words start starting with one particular word and ending with another. Successive entries in the chain must all be real words, and each can differ from the previous word by just one letter. For example, you can get from "cat" to "dog" using the following chain.
 
    cat
    cot
    cog
    dog
 
The objective of this kata is to write a program that accepts start and end words and, using words from the dictionary, builds a word chain between them. Find the shortest word chain that solves each puzzle

You can turn lead into gold with the following chain:
     
    lead
    load
    goad
    gold
 
Try to capture the developer mindset and record how you to got to you solution.

Development process


How did I get to my solution ?  It's all about covergence.  I start out and see where it goes, see what ideas pop up as I go along, see what sticks etc.  My generate approach is as follows:

1) Write Tests - this gets me thinking about how should I represent the data, and helps drive the interface.
2) Start writing code
3) Get to a working solution (it's not always pretty at this point)
4) Refactor Refactor Refactor until I hit a universal truth ie. the code becomes so simple it must be true!

To solve a problem I generally have a series of insghts - by this I mean ideas which seems obviously the right thing to do and possibly are a deep truth of the solution.

Definitions:

distance - Given a "from" word and a "target" the distance is defined to be the number letters which would need to be changed in the "from" word to mutate it to the "target" word.  ie.  cat -> bot has a distance of 2

Here we go:


Write the unit tests - this brings the first decision - should I use List<String>'s or arrays [] to represent the dictionary? Choose [] as it makes the unit tests slightly more readable.

Thought - its going to be difficult testing with a large dictionary having to search through 1000's of words, multiple roots through the wordchain "space".


  • Insight 1: Allow the tests to define the dictionary.


Idea - find a word in the dictionary which has a distance of 1 from the current word, but also contains at least 1 letter contained in the "to" word.  ie. for the wordchain cat -> dog then cot is 1 from cat and 2 from dog.  The only downside is that this strategy will only work for some word chains...

   "cat", "cot", "cog", "dog"

coding.....

The code is getting fairly complictated - theres lots of edgecases.
Idea - I'll need to keep track of words I've tried which result in dead ends.  Can I exclude these ?  from future searched or can  I?

coding.....

What happens if I need to go to a word which has no letters in common with the target word?  This is starting to get too complicated.  What I have learned is that I some how need to keep track of what wordchains I've visited which have resulted in deadends.

Idea - I'm thinking that the most reliable solution would be to brute force this, ie. work out every possible word chain.  This will produce some long chains.  Some of the chains would be dead ends and these can be discounted. Visually a brute force approach would look like a word chain tree with branched coming of branches etc.


  • Insight 2: Think of the word chains as a tree to which we can prune dead branches.
  • Insight 3: Use self recursion
  • Insight 4: Use a stack to represent the branches


I'd need to model this tree as a Map<String, Stack<String>> with the key to the map being the root of the branch.

coding ...

This is looking better.  Can I get rid of the Map ?  I think I'm on the right track as the code is getting simpler.  The self recursion is looking good.

Amazing! - I've got something that works, my unit tests are passing.  The sef recursion involves passing a lot of things around which is not ideal, but this a break through.  I can now run refactor run my tests, refactor, run my tests etc. I can now drive hard at the solution and hopefully glean some more insights.

Time to bring in the full size dictionary.  As expected some very long chains are created 50+.  How can I reduce this ?  What about converging from either direction ?  ie dog -> cat and cat -> dog.  Interestingly coming from a different direction reduces the chain length.  The size seems to be dependent on the ordering of the dictionary.

Idea -> Generate a word chain which may contain 30 words.  Randomly shuffle the words then use this list to form the dictionary for another run and so on.  How many times would I need try.  This would by an interesting experiment - I could converge on a word chain by pure luck :)


  • Insight 5:  Backup the chain when the number of words in the chain exceeds some limit


In this way the algorithm will do the pruning for me.  1 line of code !!!
 
if ( stack.size() > maxDepth ) return false;  // prune checkout

Refactor refactor refactor...


I'm going to split the code up so that the algorithm is concentrated in once class to demonstrate the simplicity of the solution.  The recursive algorithm itself is defined in 10 lines of code.

enough.... :)

Here's my solution


public class WordchainImpl implements Wordchain {
    private String [] dict;
    private int depth;

    public WordchainImpl(String[] wordList, int depth) {
        this.dict = wordList;
        this.depth = depth;
    }

    public WordchainImpl(String[] wordList) {
        this(wordList, 10);
    }


    public String [] getChain( String from, String to ) {
        // check that from and to exists
        if ( ! ( dictHas(from) && dictHas(to) ) ) return new String[0];

        for ( int i=0; i < depth; i++ ) {
            Stack stack = new Stack<>();
            recurse( from, to, stack, i );
            if ( stack.size() != 1 ) return stack.toArray( new String[stack.size()] );
        }
        return new String[0];
    }

    private boolean recurse( String from, String to, Stack stack, int maxDepth ) {
        stack.push( from );
        if( stack.size() > maxDepth ) return false;  // prune
        if ( from.equals( to) ) return true;

        for ( String w: dict) {
            if ( w.length() == from.length() && dif( from, w ) == 1 && !stack.contains(w) ) {
                if ( recurse( w, to, stack, maxDepth ) ) return true;
                stack.pop();
            }
        }
        return false;
    }

    private int dif( String a, String b ) {
        int dif = 0;
        for ( int i=0; i< a.length(); i++ ) {
            if ( a.charAt(i) != b.charAt(i) ) dif++;
        }
        return dif;
    }

    private boolean dictHas(String word) {
        for ( String s: dict) {
            if ( s.equals( word ) ) return true;
        }
        return false;
    }
}

Build Instructions

git checkout https://github.com/coder36/wordchains.git
cd wordchains
mvn clean install
java -cp target/wordchain-1.0-SNAPSHOT.jar org.coder36.wordchain.FindMyWordchain

gold
goad
load
lead

Wednesday, 13 March 2013

BDD with Cucumber

Introduction

Behavioral Driven Development (BDD) in simple terms, allows business owners, developers and testers to collaboratively work together, bound by a "User Story".  User stories are documented in feature files using the gherkin language, along with "scenarios" which when exercised prove that the feature is ready for production.

This blog entry describes a typical setup which can realise the technical aspect of BDD, user stories and automated testing of "scenarios".  Cucumber was originally written in ruby but has been ported to other languages including java.  Here we will look at the Ruby implementation provided by CucumberCapybara and Rspec.


Ruby

Assuming your are working on windows, download a ruby installer from here and install.  Run:

gem install cucumber
gem install rspec
gem install capybara

Make sure that you run these commands from the "Command Prompt with Ruby and Rails".

At this point you should have a workable ruby environment.

IDE

Install the awesome Sublime Text 2.  This will form your gherkin development environment.  Sublime is an excellent editor, blisteringly fast and beautiful.  Start by installing the package manager:
http://wbond.net/sublime_packages/package_control

Using the package manager install:
RubyTest   (https://github.com/maltize/sublime-text-2-ruby-tests)

The following package is not available in the package manager, so needs to be installed by hand.  This provides feature file syntax highlighting:

cd c:/users/<user>/appdata/Roaming/Sublime Text 2/Packages
git clone http://github.com/npverni/cucumber-sublime2-bundle Cucumber

For more packages see http://wbond.net/sublime_packages/community





The following steps can all be done via Sublime without going near the command line.

Setup

Setup the cucumber directory structure:

demo/features
demo/features/step_definitions
demo/features/step_definitions/support


demo/features/search.feature:
 Feature: Search the internet for news
 In order to find news
 As an internet user
 I want to be able to search for news web sites

 Scenario: Search for news providers
 Given I am at http://www.google.co.uk
 And I enter bbc into the search bar
 When I hit search
 Then I will be presented with a list of hits including BBC - Homepage
 When I select the BBC - Homepage link
 Then I will be navigated to website with title BBC - Homepage

This feature file is written in the "Gherkin" language.   This serves as documentation, requirements and test acceptance criteria and  can be written by business owners.  See here for a fuller description of Cucumber.   

Next we will write the step definitions mapping on to each of the "Given ...". "When..." and "Then" in the feature file.

demo/features/step_definitions/step_defs.rb
 Given "I am at $url" do |url|  
      visit url  
 end  
   
 Given "I enter $text into the search bar" do |text|  
      fill_in 'gbqfq', :with => text   
 end  
   
 When "I hit search" do  
      click_button 'gbqfb'  
 end  
   
 Then "I will be presented with a list of hits including $text" do |text|  
      page.should have_content text   
 end       
   
 When "I select the $text link" do |text|  
      click_link text  
 end  
   
 Then "I will be navigated to website with title $title" do |title|  
      page.should have_xpath("//title", :text => title)  
 end  

The visitfill_inclick_button, click_link commands are from the Capybara DSL, and are used to drive a browser, via selenium. page.should  comes from Rspec It's all about driving the web page, then testing for content.  Notice that the step_defs are readable, and very obvious.

demo/features/step_definitions/support/env.rb
 require 'capybara'  
 require 'capybara/cucumber'  
 require 'capybara/rspec'  
   
 Before do  
      Capybara.register_driver :selenium do |app|  
       Capybara::Selenium::Driver.new(app, :browser => :chrome)  
      end  
   
      Capybara.current_driver = :selenium  
 end  

This is the glue which wires everything together and keeps the step_defs clean of clutter:  "Before" is a cucumber hook which runs immediately before the scenario starts executing.  We're using this to configure selenium to use the chrome driver.

Chromium driver

Download the latest Chromium Driver chromium driver and extract into C:/Windows.  Make sure that your chrome browser is up to date.

Testing

Now everything is set up, open a ruby window:
cd demo
cucumber

You should see the chrome browser open up, navigate to www.google.co.uk, enter bbc into the search panel, then navigate over to the bbc website. Here is the cucumber output:


You can also run the cucumber tests via Sublime - select Tools->Ruby Test->Run all Tests/Feature

Notes

I developed this tutorial on a particularly slow internet connection, and I noticed that occasionally, seleniun complained that the browser window was not available (this normally happened on the "Then I will be navigated to website with title BBC - Homepage"  step definition).  I suspect this was a timeout issue.   

I don't like the way selenium dumps output to the command line, interfering with the nicely formatted cucumber output.  There must be a way to stop this?

Monday, 4 February 2013

JBoss 7 logging tips


To fully embrace jboss 7, use the jboss logging mechanism to specify categories and handlers.  It's not the easiest thing to get working, so I've put together a few hints and tips.

1) Use slf4j-api.jar as the logging facade.  Update the dependencies in the pom.xml to exclude everything else.

Be thorough as the slightest presence of log4j or any other logger, overrides the jboss logging mechanism.  In practice the easiest way is to do this it to use Eclipse to view the pom.xml "Dependency Hierarchy" and exclude any other loggers.   

2) Make sure log4j.properties is not on the classpath of the deployment.  Jboss uses log4j.properties by default - I don't know why!

3) Once and handler has been added, restart the server.

4) If logging is still not working, add the following system property:
org.jboss.as.logging.per-deployment=false