The worst code I ever wrote (maybe)
Tomas Doran
@bobtfish
tdoran@yelp.com
15/11/2013
Key lessons
!

•‘Working’ != ‘done’
– I’m a terrible person
– Working now doesn’t mean you can maintain it

•Functions for reuse
– and my horrible propensity for inline templating

•exec {} abuse
– Lets fit an entire shell script in the command => “…”

•Looping
– Hacks you have probably used.
– Doing it sanely with puppet >= 3.2

tl;dl: This talk is about bad puppet code, and how and why to do it better. The worst code I ever wrote is a
lie ;)
We’re going to concentrate on 2 main topics - functions and looping, with a nod to exec abuse.
Managing ssh keys - gitolite
!

• Legacy user management code
–Old non-modular code
–Have to fit in
–Can’t change everything

• Built in sshkey {}
– Not suitable for our requirements

We’re rapidly updating our puppet codebase, but the user management code is one of the most complex
parts, so we wanted to be minimally invasive.. We needed to generate a very custom authorized keys files to
integrate gitolite authentication into our current model.
sshkey resource

The built in sshkey {} isn’t perfect for everyone.
Most of the solutions on forge aren’t generic
http://forge.puppetlabs.com/hetzeneckerl/ssh_authorized_key

These are the core issues.
But this code didn’t work for me.
http://forge.puppetlabs.com/nightfly/ssh_keys
!

• Legacy user management code
!

–Old non-modular code
–Have to fit in

• Almost
– But not quite suitable for our requirements

Doing it myself as a concat {} seemed sane, especially given other people’s evidence..
Managing ssh keys - gitolite
!

• Legacy user management code

!

–Old non-modular code
–Have to fit in
–Can’t change everything

• Built in sshkey {}
– Not suitable for our requirements
!

• Hack what we want
with define
So, we’re gonna use a bunch of defines and concat {}, easy?
Inputs and outputs
!

gitolite::user { ‘tdoran’:
key_source => $key_source;
}
– Add in current user management code
– $keysource = puppet:///files/
users/tdoran/authorized_keys

/etc/authorized_keys.d/git.pub
– Use concat
– Multiple gitolite instances!

- API for creating a key we drop into our current user management path
- Eventual generated file, from a concat {} resource
- Note we can have multiple gitolite instances on one box, that makes everything much harder.
So how does it work?
First, get an array of key lines, with names prepended

$864
Split the key into an array

$864

We split the file of keys into an array of lines.
We remove comments, whitespace only lines etc
We capture the entire key passed in.
Split the key into an array

$864

We interpolate the username in front of that key, space separating them
Split the key into an array

$864

End up with an array of strings which are username and key joined by a space
We then abuse a define as a loop by calling it with the array of lines as $title
Prepend instance name
!

Add user to every instance
– [“${username} ${restofkey}”]
– [“${instance} “]
– [“${instance} ${username} ${restofkey}”]
– Use this as $name in a define to iterate
– ssh/authorized_keys.d/${instance}.pub

We then have an array of instances, and an array of users/keys.
Our array_prefix function makes the product of both lists.
Complexity due to iterating over two lists $key_lines and $instances
Prepend instance name
!

Add user to every instance
– [“${username} ${restofkey}”]
– [“${instance} “]
– [“${instance} ${username} ${restofkey}”]
– Use this as $name in a define to iterate
– ssh/authorized_keys.d/${instance}.pub

We then have an array of instances, and an array of users/keys.
Our array_prefix function makes the product of both lists.
Complexity due to iterating over two lists $key_lines and $instances
Prepend instance name
!

Add user to every instance
– [“${username} ${restofkey}”]
– [“${instance} “]
– [“${instance} ${username} ${restofkey}”]
– Use this as $name in a define to iterate
– ssh/authorized_keys.d/${instance}.pub

We then have an array of instances, and an array of users/keys.
Our array_prefix function makes the product of both lists.
Complexity due to iterating over two lists $key_lines and $instances
Prepend instance name
!

Add user to every instance
– [“${username} ${restofkey}”]
– [“${instance} “]
– [“${instance} ${username} ${restofkey}”]
– Use this as $name in a define to iterate
– ssh/authorized_keys.d/${instance}.pub

We then have an array of instances, and an array of users/keys.
Our array_prefix function makes the product of both lists.
Complexity due to iterating over two lists $key_lines and $instances
Prepend instance name
!

Add user to every instance
– [“${username} ${restofkey}”]
– [“${instance} “]
– [“${instance} ${username} ${restofkey}”]
– Use this as $name in a define to iterate
– ssh/authorized_keys.d/${instance}.pub

This is gross
We then have an array of instances, and an array of users/keys.
Our array_prefix function makes the product of both lists.
Complexity due to iterating over two lists $key_lines and $instances
My original next code
Stare in horror!

$864

This was hideous. Whilst looping without define abuse is hard, there’s just no excuse for this.
Sins

LOL??

Hideous abuse of define - one $name for
each $key_lines

$864

We then split the line back up.
Sins

$864

Shift the user name off the array.
Lol, who knew you could do that?
Use the remaining data joined back up

LOL??

Hideous abuse of variables
Sins
Hideous abuse of interpolation

LOL??
LOL??

$864

So, here, I have a double quoted string containing erb, and I’m then using double quote (not erb)
interpolation.. Niiice.
Sins
Hideous abuse of quoting

EVERY TYPE OF QUOTES!!!

LOL??

LOL??

LOL??

$864
Sins
Hideous abuse of quoting

$864

LOL??

LOL??
Sins
Hideous abuse of bash

LOL??
$864

ssh-keygen can’t be fed from stdin without disgusting trickery (modern bash specific)
Sins
So complex I’m tempting the template!

LOL??
$864

LOL??

And yes, it’s so complex I template the thing I’m interpolating into a template..
I am a bad person
LOL?

$864
I am a bad person
LOL?

$864
I am a bad person
LOL?

$864

Thankfully
I am a bad person
LOL?

$864

Someone stopped me ;)
Don’t abuse inline_template()
!

• Please?
– I’m a terrible person
– Working now doesn’t mean you can maintain it

• Functions for reuse
– Full power of ruby
– Composable
– There should be more libraries of functions

Do what I’m saying, not what I do :)
More sane

Except each user can have multiple keys, and we want to prevent any key being reused by multiple
users.
Issues with functions
!

• What functions do I even have?
– Hard to know what functions you have imported
– stdlib, builtins, ….your $modulepath here…

• What do these even do?
– Often need to read the code to determine
– Lots of functions packaged with modules - don’t do
this!

• Millions of twisty little functions, all alike
• Millions of twisty little functions, all different
– You know what this reminds me of?
Everyone’s favorite language

If all you have is a hammer, everything looks like a nail
has a few functions

Unfortunately, there are no consistent naming conventions
arguably

And it’s hard to work out which function you actually want
a few too many..

The problem is, functions in a global namespace aren’t all that modular/composable.
You can call functions from functions - but dependency hell
we’re almost half way through….

Shall I stop now? :)
puppet problem is worse than this - what functions you have depends on what modules are in your codebase.
Get docs for project’s functions

I wrote a thing to help with this - this will document _your_ functions for you. gist at end of talk.
gitolite group file generation
!

• /etc/groups on system
!

• gitolite groups have a different
format:
@admins = tdoran joebloggs!
!

• Use exec {… add mess here … }

This is another great example of what not to do.
exec {} abuse
Syntax highlighting won’t save you now puny human!

$864

This. DO NOT DO THIS.
Please?
exec {} abuse
Syntax highlighting won’t save you now puny human!

$864
exec {} abuse
Syntax highlighting won’t save you now puny human!

$864
exec {} abuse
Syntax highlighting won’t save you now puny human!

$864

Arggh, the backslashes!
This madness actually worked
LOL?

$864
This madness actually worked
LOL?

!

•exec {} abuse

– Lets fit an entire shell script in the 

command => “…”
– Don’t do it! :)
– Drop a perl/python/ruby script (or two)
instead, call them from the exec.
$864
– Think about writing a type/provider

– Lots of examples of parsedfile available
Which would you rather have to debug?

$864
OR THIS!?!

$864
Which one’s going to stay debugged?

$864
OR THIS?!?

$864
Looping sanely

!

This would work fine.
Need multiple instances :(
—parser future
—parser future

$864
—parser future
!

• Can’t do this at Yelp
(yet)

• Still using 2.7, because
$864
variable scope
Hypocrisy avoidance
!

”There should be more libraries
of functions”
- Me, about 5m ago

– https://github.com/bobtfish/puppetsshkey_utilities
– https://github.com/bobtfish/puppet-better_file
– https://gist.github.com/bobtfish/7403811
– Whole module to follow (eventually)
That’s all folks
!

• Where was that code?
– https://github.com/bobtfish/puppet-sshkey_utilities
– https://github.com/bobtfish/puppet-better_file

• Did you say you were hiring?
– YES
– Hamburg.
– London.
– San Francisco.

• Questions?

The Worst Code I Ever Wrote

  • 2.
    The worst codeI ever wrote (maybe) Tomas Doran @bobtfish tdoran@yelp.com 15/11/2013
  • 3.
    Key lessons ! •‘Working’ !=‘done’ – I’m a terrible person – Working now doesn’t mean you can maintain it •Functions for reuse – and my horrible propensity for inline templating •exec {} abuse – Lets fit an entire shell script in the command => “…” •Looping – Hacks you have probably used. – Doing it sanely with puppet >= 3.2 tl;dl: This talk is about bad puppet code, and how and why to do it better. The worst code I ever wrote is a lie ;) We’re going to concentrate on 2 main topics - functions and looping, with a nod to exec abuse.
  • 4.
    Managing ssh keys- gitolite ! • Legacy user management code –Old non-modular code –Have to fit in –Can’t change everything • Built in sshkey {} – Not suitable for our requirements We’re rapidly updating our puppet codebase, but the user management code is one of the most complex parts, so we wanted to be minimally invasive.. We needed to generate a very custom authorized keys files to integrate gitolite authentication into our current model.
  • 5.
    sshkey resource The builtin sshkey {} isn’t perfect for everyone. Most of the solutions on forge aren’t generic
  • 6.
    http://forge.puppetlabs.com/hetzeneckerl/ssh_authorized_key These are thecore issues. But this code didn’t work for me.
  • 7.
    http://forge.puppetlabs.com/nightfly/ssh_keys ! • Legacy usermanagement code ! –Old non-modular code –Have to fit in • Almost – But not quite suitable for our requirements Doing it myself as a concat {} seemed sane, especially given other people’s evidence..
  • 8.
    Managing ssh keys- gitolite ! • Legacy user management code ! –Old non-modular code –Have to fit in –Can’t change everything • Built in sshkey {} – Not suitable for our requirements ! • Hack what we want with define So, we’re gonna use a bunch of defines and concat {}, easy?
  • 9.
    Inputs and outputs ! gitolite::user{ ‘tdoran’: key_source => $key_source; } – Add in current user management code – $keysource = puppet:///files/ users/tdoran/authorized_keys /etc/authorized_keys.d/git.pub – Use concat – Multiple gitolite instances! - API for creating a key we drop into our current user management path - Eventual generated file, from a concat {} resource - Note we can have multiple gitolite instances on one box, that makes everything much harder.
  • 10.
    So how doesit work? First, get an array of key lines, with names prepended $864
  • 11.
    Split the keyinto an array $864 We split the file of keys into an array of lines. We remove comments, whitespace only lines etc We capture the entire key passed in.
  • 12.
    Split the keyinto an array $864 We interpolate the username in front of that key, space separating them
  • 13.
    Split the keyinto an array $864 End up with an array of strings which are username and key joined by a space We then abuse a define as a loop by calling it with the array of lines as $title
  • 14.
    Prepend instance name ! Adduser to every instance – [“${username} ${restofkey}”] – [“${instance} “] – [“${instance} ${username} ${restofkey}”] – Use this as $name in a define to iterate – ssh/authorized_keys.d/${instance}.pub We then have an array of instances, and an array of users/keys. Our array_prefix function makes the product of both lists. Complexity due to iterating over two lists $key_lines and $instances
  • 15.
    Prepend instance name ! Adduser to every instance – [“${username} ${restofkey}”] – [“${instance} “] – [“${instance} ${username} ${restofkey}”] – Use this as $name in a define to iterate – ssh/authorized_keys.d/${instance}.pub We then have an array of instances, and an array of users/keys. Our array_prefix function makes the product of both lists. Complexity due to iterating over two lists $key_lines and $instances
  • 16.
    Prepend instance name ! Adduser to every instance – [“${username} ${restofkey}”] – [“${instance} “] – [“${instance} ${username} ${restofkey}”] – Use this as $name in a define to iterate – ssh/authorized_keys.d/${instance}.pub We then have an array of instances, and an array of users/keys. Our array_prefix function makes the product of both lists. Complexity due to iterating over two lists $key_lines and $instances
  • 17.
    Prepend instance name ! Adduser to every instance – [“${username} ${restofkey}”] – [“${instance} “] – [“${instance} ${username} ${restofkey}”] – Use this as $name in a define to iterate – ssh/authorized_keys.d/${instance}.pub We then have an array of instances, and an array of users/keys. Our array_prefix function makes the product of both lists. Complexity due to iterating over two lists $key_lines and $instances
  • 18.
    Prepend instance name ! Adduser to every instance – [“${username} ${restofkey}”] – [“${instance} “] – [“${instance} ${username} ${restofkey}”] – Use this as $name in a define to iterate – ssh/authorized_keys.d/${instance}.pub This is gross We then have an array of instances, and an array of users/keys. Our array_prefix function makes the product of both lists. Complexity due to iterating over two lists $key_lines and $instances
  • 19.
    My original nextcode Stare in horror! $864 This was hideous. Whilst looping without define abuse is hard, there’s just no excuse for this.
  • 20.
    Sins LOL?? Hideous abuse ofdefine - one $name for each $key_lines $864 We then split the line back up.
  • 21.
    Sins $864 Shift the username off the array. Lol, who knew you could do that? Use the remaining data joined back up LOL?? Hideous abuse of variables
  • 22.
    Sins Hideous abuse ofinterpolation LOL?? LOL?? $864 So, here, I have a double quoted string containing erb, and I’m then using double quote (not erb) interpolation.. Niiice.
  • 23.
    Sins Hideous abuse ofquoting EVERY TYPE OF QUOTES!!! LOL?? LOL?? LOL?? $864
  • 24.
    Sins Hideous abuse ofquoting $864 LOL?? LOL??
  • 25.
    Sins Hideous abuse ofbash LOL?? $864 ssh-keygen can’t be fed from stdin without disgusting trickery (modern bash specific)
  • 26.
    Sins So complex I’mtempting the template! LOL?? $864 LOL?? And yes, it’s so complex I template the thing I’m interpolating into a template..
  • 27.
    I am abad person LOL? $864
  • 28.
    I am abad person LOL? $864
  • 29.
    I am abad person LOL? $864 Thankfully
  • 30.
    I am abad person LOL? $864 Someone stopped me ;)
  • 31.
    Don’t abuse inline_template() ! •Please? – I’m a terrible person – Working now doesn’t mean you can maintain it • Functions for reuse – Full power of ruby – Composable – There should be more libraries of functions Do what I’m saying, not what I do :)
  • 32.
    More sane Except eachuser can have multiple keys, and we want to prevent any key being reused by multiple users.
  • 33.
    Issues with functions ! •What functions do I even have? – Hard to know what functions you have imported – stdlib, builtins, ….your $modulepath here… • What do these even do? – Often need to read the code to determine – Lots of functions packaged with modules - don’t do this! • Millions of twisty little functions, all alike • Millions of twisty little functions, all different – You know what this reminds me of?
  • 34.
    Everyone’s favorite language Ifall you have is a hammer, everything looks like a nail
  • 35.
    has a fewfunctions Unfortunately, there are no consistent naming conventions
  • 36.
    arguably And it’s hardto work out which function you actually want
  • 37.
    a few toomany.. The problem is, functions in a global namespace aren’t all that modular/composable. You can call functions from functions - but dependency hell
  • 38.
    we’re almost halfway through…. Shall I stop now? :) puppet problem is worse than this - what functions you have depends on what modules are in your codebase.
  • 39.
    Get docs forproject’s functions I wrote a thing to help with this - this will document _your_ functions for you. gist at end of talk.
  • 40.
    gitolite group filegeneration ! • /etc/groups on system ! • gitolite groups have a different format: @admins = tdoran joebloggs! ! • Use exec {… add mess here … } This is another great example of what not to do.
  • 41.
    exec {} abuse Syntaxhighlighting won’t save you now puny human! $864 This. DO NOT DO THIS. Please?
  • 42.
    exec {} abuse Syntaxhighlighting won’t save you now puny human! $864
  • 43.
    exec {} abuse Syntaxhighlighting won’t save you now puny human! $864
  • 44.
    exec {} abuse Syntaxhighlighting won’t save you now puny human! $864 Arggh, the backslashes!
  • 45.
    This madness actuallyworked LOL? $864
  • 46.
    This madness actuallyworked LOL? ! •exec {} abuse – Lets fit an entire shell script in the 
 command => “…” – Don’t do it! :) – Drop a perl/python/ruby script (or two) instead, call them from the exec. $864 – Think about writing a type/provider – Lots of examples of parsedfile available
  • 47.
    Which would yourather have to debug? $864
  • 48.
  • 49.
    Which one’s goingto stay debugged? $864
  • 50.
  • 51.
    Looping sanely ! This wouldwork fine. Need multiple instances :(
  • 52.
  • 53.
  • 54.
    —parser future ! • Can’tdo this at Yelp (yet) • Still using 2.7, because $864 variable scope
  • 55.
    Hypocrisy avoidance ! ”There shouldbe more libraries of functions” - Me, about 5m ago – https://github.com/bobtfish/puppetsshkey_utilities – https://github.com/bobtfish/puppet-better_file – https://gist.github.com/bobtfish/7403811 – Whole module to follow (eventually)
  • 56.
    That’s all folks ! •Where was that code? – https://github.com/bobtfish/puppet-sshkey_utilities – https://github.com/bobtfish/puppet-better_file • Did you say you were hiring? – YES – Hamburg. – London. – San Francisco. • Questions?