56

The Worst Code I Ever Wrote

Embed Size (px)

DESCRIPTION

"The Worst Code I Ever Wrote" by Tomas Doran of Yelp at Puppet Camp London 2013. Find the video here: http://puppetlabs.com/community/puppet-camp

Citation preview

Page 1: The Worst Code I Ever Wrote
Page 2: The Worst Code I Ever Wrote

The worst code I ever wrote (maybe)

Tomas Doran

@bobtfish

[email protected]

15/11/2013

Page 3: The Worst Code I Ever Wrote

!•‘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

Key lessons

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.

Page 4: The Worst Code I Ever Wrote

!

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

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

Managing ssh keys - gitolite

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.

Page 5: The Worst Code I Ever Wrote

sshkey resource

The built in sshkey {} isn’t perfect for everyone. Most of the solutions on forge aren’t generic

Page 6: The Worst Code I Ever Wrote

http://forge.puppetlabs.com/hetzeneckerl/ssh_authorized_key

These are the core issues. But this code didn’t work for me.

Page 7: The Worst Code I Ever Wrote

!

• Legacy user management code –Old non-modular code –Have to fit in

!

• Almost – But not quite suitable for our requirements

http://forge.puppetlabs.com/nightfly/ssh_keys

Doing it myself as a concat {} seemed sane, especially given other people’s evidence..

Page 8: The Worst Code I Ever Wrote

!

• 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

Managing ssh keys - gitolite

So, we’re gonna use a bunch of defines and concat {}, easy?

Page 9: The Worst Code I Ever Wrote

!

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!

Inputs and outputs

- 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.

Page 10: The Worst Code I Ever Wrote

So how does it work?

$864

First, get an array of key lines, with names prepended

Page 11: The Worst Code I Ever Wrote

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.

Page 12: The Worst Code I Ever Wrote

Split the key into an array

$864

We interpolate the username in front of that key, space separating them

Page 13: The Worst Code I Ever Wrote

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

Page 14: The Worst Code I Ever Wrote

!

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

Prepend instance name

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

Page 15: The Worst Code I Ever Wrote

!

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

Prepend instance name

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

Page 16: The Worst Code I Ever Wrote

!

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

Prepend instance name

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

Page 17: The Worst Code I Ever Wrote

!

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

Prepend instance name

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

Page 18: The Worst Code I Ever Wrote

!

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

Prepend instance name

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

Page 19: The Worst Code I Ever Wrote

My original next code

$864

Stare in horror!

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

Page 20: The Worst Code I Ever Wrote

Sins

$864

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

LOL?

?We then split the line back up.

Page 21: The Worst Code I Ever Wrote

Sins

$864

Hideous abuse of variables

LOL??

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

Page 22: The Worst Code I Ever Wrote

Sins

$864

Hideous abuse of interpolation

LOL??

LOL??

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

Page 23: The Worst Code I Ever Wrote

Sins

$864

Hideous abuse of quoting

LOL?

?

LOL?

?

LOL??

EVERY TYPE OF QUOTES!!!

Page 24: The Worst Code I Ever Wrote

Sins

$864

Hideous abuse of quoting

LOL?? LOL??

Page 25: The Worst Code I Ever Wrote

Sins

$864

Hideous abuse of bash

LOL??

ssh-keygen can’t be fed from stdin without disgusting trickery (modern bash specific)

Page 26: The Worst Code I Ever Wrote

Sins

$864

So complex I’m tempting the template!

LOL??

LOL??

And yes, it’s so complex I template the thing I’m interpolating into a template..

Page 27: The Worst Code I Ever Wrote

I am a bad person

$864

LOL?

Page 28: The Worst Code I Ever Wrote

I am a bad person

$864

LOL?

Page 29: The Worst Code I Ever Wrote

I am a bad person

$864

LOL?

Thankfully

Page 30: The Worst Code I Ever Wrote

I am a bad person

$864

LOL?

Someone stopped me ;)

Page 31: The Worst Code I Ever Wrote

!• 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

Don’t abuse inline_template()

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

Page 32: The Worst Code I Ever Wrote

More sane

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

Page 33: The Worst Code I Ever Wrote

!• 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?

Issues with functions

Page 34: The Worst Code I Ever Wrote

Everyone’s favorite language

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

Page 35: The Worst Code I Ever Wrote

has a few functions

Unfortunately, there are no consistent naming conventions

Page 36: The Worst Code I Ever Wrote

arguably

And it’s hard to work out which function you actually want

Page 37: The Worst Code I Ever Wrote

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

Page 38: The Worst Code I Ever Wrote

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.

Page 39: The Worst Code I Ever Wrote

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.

Page 40: The Worst Code I Ever Wrote

!

• /etc/groups on system !

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

• Use exec {… add mess here … }

gitolite group file generation

This is another great example of what not to do.

Page 41: The Worst Code I Ever Wrote

exec {} abuse

$864

Syntax highlighting won’t save you now - puny human!

This. DO NOT DO THIS. Please?

Page 42: The Worst Code I Ever Wrote

exec {} abuse

$864

Syntax highlighting won’t save you now - puny human!

Page 43: The Worst Code I Ever Wrote

exec {} abuse

$864

Syntax highlighting won’t save you now - puny human!

Page 44: The Worst Code I Ever Wrote

exec {} abuse

$864

Syntax highlighting won’t save you now - puny human!

Arggh, the backslashes!

Page 45: The Worst Code I Ever Wrote

This madness actually worked

$864

LOL?

Page 46: The Worst Code I Ever Wrote

This madness actually worked

$864

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.

– Think about writing a type/provider

– Lots of examples of parsedfile available

Page 47: The Worst Code I Ever Wrote

Which would you rather have to debug?

$864

Page 48: The Worst Code I Ever Wrote

OR THIS!?!

$864

Page 49: The Worst Code I Ever Wrote

Which one’s going to stay debugged?

$864

Page 50: The Worst Code I Ever Wrote

OR THIS?!?

$864

Page 51: The Worst Code I Ever Wrote

!This would work fine.

Need multiple instances :(

Looping sanely

Page 52: The Worst Code I Ever Wrote

—parser future

Page 53: The Worst Code I Ever Wrote

—parser future

$864

Page 54: The Worst Code I Ever Wrote

—parser future

$864

!

• Can’t do this at Yelp (yet)

• Still using 2.7, because variable scope

Page 55: The Worst Code I Ever Wrote

!

”There should be more libraries of functions”

- Me, about 5m ago

– https://github.com/bobtfish/puppet-sshkey_utilities

– https://github.com/bobtfish/puppet-better_file

– https://gist.github.com/bobtfish/7403811

– Whole module to follow (eventually)

Hypocrisy avoidance

Page 56: The Worst Code I Ever Wrote

!

• 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?

That’s all folks