Yak Shaving

Development — Dillon @ 11:14 am


Let’s say you’re trying to be nice to your wife/girlfriend/whoever and want to get the coffee in the morning before they wake up. So you set out to get a cup of coffee.

First, you can’t find your keys. Then you remembered that you loaned your house key to your neighbor Alice while you were out of town last week and you never got them back. You don’t want to wake up your special someone so you go to your neighbor Alice’s house and knock on the door. Alice says, “good morning!”
“Do you still have my house key?”, you say.
“It’s on my car’s keyring and my car is at my brother Barry’s house”, they reply.
“Where is your brother?”
“Oh, he’s across the street.”

So you walk across the street and try to find Barry. He’s pulling out of the driveway and he says “hop in!”.
“Well, actually I’m trying to get my car keys back”, you say.
“I’m in a rush, I need to deliver this couch to my friend Calvin who’s moving today. Would you mind helping me unload this and then I’ll give you your key back.”
“Sure Barry.”

So you unload the couch at Calvin’s house but when you’re putting the couch down, you rip a pillow. Calvin is none too happy.
“These pillows were stuffed with my finest Yak hair! You’re going to have to fix them”, says Calvin.
Barry says, “you get more stuffing and I’ll go get my needle and thread kit from my house”.

Barry leaves in his truck. Calvin gives you some yak shears and points you to his private Yak holding pen. Calvin goes inside and has some bacon and eggs. You sit down on a Yak shaving stool and start shaving a Yak on a Saturday morning. Suddenly you stand up and say, “why the frak am I shaving a Yak?!”.

“I just wanted some coffee!”

Yak Shaving is when you find yourself at the end of a long chain of implied tasks and find yourself suddenly shaving a Yak. You’ll find it a lot in technical tasks and projects but you’ll also find it in errands and daily life. Yak Shaving is wasted time or possibly some delusion of productivity. You should be having coffee in bed with your sweetheart but instead you’re loping off locks of Yak hair.

So let’s summarize our little tale and how it would end.

  • You can’t find your keys because Alice has them.
  • Alice sends you to Barry’s house.
  • Barry takes you to Calvin’s house.
  • You unload a couch and rip a Yak pillow.
  • Calvin makes you shave a Yak.
  • You finish shaving a Yak.
  • Barry comes back and fixes the pillow.
  • Barry takes you home and gives you your key back.
  • You get coffee for your sweetheart.
  • Your sweetheart can’t believe you shaved a Yak today.

Notice that Yak shaving happens at step #5 but there are 10 steps total. That’s because we’re in a chain. Just because you shave the Yak doesn’t mean you get your coffee. You still have to exit the chain which at the very least is N steps back out. Maybe on the way to get coffee, your car breaks (N+X). Who knows. So to avoid getting deep into a Yak chain, you need to break out of it as soon as possible. For example, tell Alice to get your keys as soon as possible and wake up your sweetheart and ask her where her keys are. Yes, it’s sub-optimal. But don’t assume that your sweetheart is going to be mad about being woken up. You could say, “honey I’m going out to get you a treat, where are your keys?”. It’s not that you should predict the future and assume that steps past Alice are going to be chaining, long and Yak-y. You should instead think of branching alternatives immediately and not just blindly follow Alice’s instructions.

Ok, so let’s forget about solving or avoiding the Yak problem. Let’s get into the meat of this. Let’s implement this chain in a few ways.

Ruby

# yak.rb
def get_coffee
        # getting coffee means two things, keys + car
        puts "I don't have my keys..."
        find_keys
        drive_to_coffee_shop
        puts "Got coffee."
end
 
def find_keys
        puts "Alice has my keys..."
        find_alice
        puts "Ok got keys."
end
 
def find_alice
        puts "Alice says Barry has my keys."
        find_barry
end
 
def find_barry
        puts "Barry wants me to help him unload a couch ..."
        unload_couch
        puts "Barry took me home and gave me my keys."
end
 
def unload_couch
        puts "I just ripped a Yak pillow unloading the couch."
        shave_yak
        puts "Done unloading couch."
end
 
def shave_yak
        puts "I'm shaving a Yak!"
end
 
def drive_to_coffee_shop
        puts "Driving to coffee shop..."
end
 
def surprise_sweetheart
        puts "Surprise honey!  Coffee!"
end
 
 
# Wake up, be nice to sweetheart
puts "I should surprise my sweetheart..."
get_coffee
surprise_sweetheart
puts "Awww..."

We run it and get:
$ ruby yak.rb
I should surprise my sweetheart...
I don't have my keys...
Alice has my keys...
Alice says Barry has my keys.
Barry wants me to help him unload a couch ...
I just ripped a Yak pillow unloading the couch.
I'm shaving a Yak!
Done unloading couch.
Barry took me home and gave me my keys.
Ok got keys.
Driving to coffee shop...
Got coffee.
Surprise honey! Coffee!
Awww...

Obviously this is very procedural and just creating the chain using simple method calls. But at least notice that the “main” of the program (the bottom five lines) just calls get_coffee and surprise_sweetheart. That part is at least somewhat encapsulated and clean. If we wanted to get really fancy we could create a generic action class that would receive messages and call a proc possibly. We’re still creating this chain in code which makes it extremely brittle and one-shot. How would we use this again? How would we put this in our toolbelt?

XML
Let’s create a XML-based workflow and run it in Ruby.

Here’s our steps from the bullets in an XML doc.

<?xml version="1.0" encoding="UTF-8"?>
<steps>
  <step stepId="0" stepName="Get Coffee">
    <message>I don't have my keys...</message>
    <step stepId="1" stepName="Find Keys">
      <message>Alice has my keys...</message>
      <step stepId="2" stepName="Find Alice">
        <message>Alice says Barry has my keys.</message>
        <step stepId="3" stepName="Find Barry">
          <message>Barry wants me to help him unload a couch ...</message>
          <step stepId="4" stepName="Unload Couch">
            <message>I just ripped a Yak pillow unloading the couch.</message>
            <step stepId="5" stepName="Shave Yak">
              <message>I'm shaving a Yak!</message>
            </step>
            <message>Done unloading couch.</message>
          </step>
          <message>Barry took me home and gave me my keys.</message>
        </step>
      </step>
      <message>Ok got keys.</message>
    </step>
    <step stepId="6" stepName="Drive to Coffee Shop">
      <message>Driving to coffee shop...</message>
    </step>
    <message>Got coffee.</message>
  </step>
  <step stepId="7" stepName="Surprise Sweetheart" onDone="Awww...">
    <message>Surprise honey!  Coffee!</message>
  </step>
</steps>

And here’s some code to step through the XML doc and print what’s going on.

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
require 'rexml/document'
include REXML
 
xmlfile = File.new("yak.xml")
xmldoc = Document.new(xmlfile)
 
# Now get the root element
root = xmldoc.root
 
# hack
module REXML
  def each_child_element(&block)
    self.elements.each {|node|
      block.call(node)
    }
  end
end
 
def recurse(the_element)
  the_element.each_child_element do |childElement|
    if !childElement.attributes.empty?
      puts "[#{childElement.attributes['stepName']}]"
    else
      puts childElement.text
    end
 
    recurse(childElement)
  end
 
end  
 
recurse(xmldoc)

When we run it, we get this:

[Get Coffee]
I don't have my keys...
[Find Keys]
Alice has my keys...
[Find Alice]
Alice says Barry has my keys.
[Find Barry]
Barry wants me to help him unload a couch ...
[Unload Couch]
I just ripped a Yak pillow unloading the couch.
[Shave Yak]
I'm shaving a Yak!
Done unloading couch.
Barry took me home and gave me my keys.
Ok got keys.
[Drive to Coffee Shop]
Driving to coffee shop...
Got coffee.
[Surprise Sweetheart]
Surprise honey! Coffee!

It reads well but it’s not that great. First off, our XML file relies very heavily on the message elements being in the right order. I’d much rather have an onDone attribute on the element that would act like a callback. This is more in line with a workflow engine or something more dynamic that could execute code. I didn’t really have time to get this working. The message coming after the action is happening is supposed to signify what to do when it’s completed. This is not really that elegant. It’d be much better to create a queue and put messages onto it, then pop down the queue.

But that sounds like Yak Shaving to me …

Rails behind Enterprise SSO

Rails,Systems — Dillon @ 8:12 pm

This is a quick write-up without a lot of detail. We hacked together a quick rails app to do provisioning in the style of OIM behind an OAM SSO webgate. The complete guide and detail would be tens of pages so I’ll just give a quick overview for the strategy.

The Goal

  • Ldap authentication
  • No db
  • Sso protected
  • Weblogic deployment

Develop app steps and failures

We used activeldap for the LDAP pieces and defined our user model to narrowly search for a particular objectclass and attributes. Tried to use Authlogic. Fail. Acts as authenticated fail. Devise fail. Ended up using filters and activeldap. Integrating the gems and activeldap was actually kind of hard. A lot of the security gems assume you’ve got activerecord users and depend a lot on the validation helpers etc. So some of the authN gems didn’t work for us. We also had to hack a bit on the activeldap validations. Password policy was non-trivial. I just rolled my own like so:


def validate_password(password)
// initialize a password score
// call password rule methods like:
// check_special_characters
// check_length
// check_uppercase
end

The score from each check method is added to a total score. If the score is greater than zero then your password fails the checks. Each check, a hash for flash[:error] is used so that a precise error message is possible. It works ok except the flash error display is for some reason not ordered correctly.

All configuration constants are stored as YML as an app_config.yml file. For example, the LDAP server, port, password policy rules etc.

For the SSO config, we just detect header as HTTP_REMOTE_USER even though OAM is creating REMOTE_USER. Quick and easy. You have to append the “HTTP_” for the name. It’s a naming convention thing that you can’t do anything about. If your OAM header variable is UNICORNS, then you have to use HTTP_UNICORNS.

We used formtastic for the forms. This was a bit problematic with it trying to detect the activeldap model instead of activerecord.

Testing while developing

Ok so how do you test integration? Are you going to SSO enable your dev laptop? That’s way too hard. You can hardcode the credentials for a while but then eventually you’re going to want to test. I got around this by using a firefox plugin called modify headers. It’s pretty straight forward except for the small detail that you have to keep it open while hitting pages. I thought it would run in the background but it doesn’t. Just keep the modify headers firefox plugin open and it’ll let you create an auth cookie. Don’t worry, this isn’t a security hole. OAM in production won’t let you do this. It’s just used for development.

Warble

install warbler with gem install warbler
Generate default config warble config
Install jruby-openssl because activeldap requires it
Edit config/warble.rb to include jruby-openssl note that you don’t have to have jruby installed or anything.

The rest of the steps are not rails related. Deploy war to Weblogic as usual. Set up a Proxy webgate back to Weblogic for /app (you can’t protecte Weblogic directly with OAM). Protect /app with an OAM policy. If your firefox header test worked then when you turn that off and hit it behind OAM it will work the same. I was able to identify and trust the REMOTE_USER header coming in.

Bam, you’ve got a rails app working in a big scary enterprise SSO environment. The best part about all of this was how fast it went. Compared to JSP/Java EE dev, it was a breeze. The only big multi-day hangups we had was with activeldap. Many gems and auth models really expect you do have your user in the DB. Unfortunately, putting users in the DB creates a silo. Fine for small shops, not so good if you’re using Active Directory, OID, OpenLDAP or Fedora DS (389) for a centralized login.

RVM behind proxy and firewall

Ruby — Dillon @ 8:08 pm

Firefox worked, gem install worked, elinks worked and even curl worked. So why isn’t RVM able to update?

Initialized empty Git repository in ~/.rvm/src/rvm/.git/
github.com[0: x.x.x.x]: errno=No route to host
fatal: unable to connect a socket (No route to host)

And you’ve tried setting http_proxy and –proxy flags with rvm. No need to fear, there’s a git URL hardcoded in there.

cd ~/.rvm/scripts
vi utility

Change the line:
builtin cd $rvm_src_path && git clone git://github.com/wayneeseguin/rvm.git && builtin cd rvm/ && ./scripts/install
to
builtin cd $rvm_src_path && git clone http://github.com/wayneeseguin/rvm.git && builtin cd rvm/ && ./scripts/install

This is how I got it to work but obviously this is a hack. You should check out rvm:fw for a more permanent solution.

Alternatively, if you can get curl to work with ALL_PROXY and HTTP_PROXY environment variables (test with curl yahoo.com), then you can simply do the manual install method to install RVM or even upgrade RVM to the latest version. I used this method when I was seeing “rvm get fails” where rvm get wouldn’t work:
curl -s https://rvm.beginrescueend.com/install/rvm -o rvm-installer ; chmod +x rvm-installer ; ./rvm-installer --version latest

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2012 SQUARISM | powered by WordPress with Barecity