"the worst code i ever wrote"

56

Upload: tomas-doran

Post on 08-May-2015

4.819 views

Category:

Technology


1 download

DESCRIPTION

My talk from the pupet devops conference Berlin 2014 (http://code-your-config.com/). A low level tour of some terrible terrible puppet code, with advice on how to do it better (including showing off the awesome new each() construct you get in puppet 3.2)

TRANSCRIPT

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