890 likes | 1.03k Views
Let them configure!. a sermon on reusability. Łukasz Langa. ambv at #python-dev (FreeNode IRC) @llanga on Twitter lukasz@langa.pl as a last resort. Robert M. Pirsig. „The solutions all are simple – after you have arrived at them.
E N D
Let them configure! a sermon on reusability
Łukasz Langa ambvat #python-dev (FreeNode IRC) @llangaon Twitter lukasz@langa.plas a last resort
Robert M. Pirsig „The solutions all are simple – after you have arrived at them. But they're simple only when you know already what they are.”
Easy-ish formats [user] email = lukasz@langa.pl name= Łukasz Langa [push] default= current [color] branch= auto diff= auto interactive= auto status = auto [diff] external= git_vimdiff [pager] diff= [core] excludesfile= ~/.gitignore INI Apache nginx JSON
Easy-ish formats Listen80 User apache Groupapache DocumentRoot"/var/www/vhosts/root" <Directory /> OptionsFollowSymLinks AllowOverrideNone </Directory> <Directory "/var/www/vhosts"> OptionsIndexesFollowSymLinksMultiViewsExecCGI AllowOverrideAll AddHandler php5-fastcgi .php Action php5-fastcgi /cgi-bin/php.fcgi Order allow,deny Allow from all </Directory> <IfModulemod_userdir.c> UserDirdisable </IfModule> Includeconf.d/*.conf INI Apache nginx JSON
Easy-ish formats Listen80 User apache Groupapache DocumentRoot"/var/www/vhosts/root" <Directory /> OptionsFollowSymLinks AllowOverrideNone </Directory> <Directory "/var/www/vhosts"> OptionsIndexesFollowSymLinksMultiViewsExecCGI AllowOverrideAll AddHandler php5-fastcgi .php Action php5-fastcgi /cgi-bin/php.fcgi Order allow,deny Allow from all </Directory> <IfModulemod_userdir.c> UserDirdisable </IfModule> Includeconf.d/*.conf INI Apache nginx JSON What’s the difference: Options +IndexesIncludesMultiViews OptionsIndexesIncludesMultiViews
Easy-ish formats server { listen 80; server_named.com *.d.com; rewrite ^ http://www.d.com $request_uri? permanent; } server { listen 80 default; server_namewww.d.com; indexindex.html; root /home/d.com; location /forum { rewrite forum(.*) http:// forum.d.com$1 permanent; } } INI Apache nginx JSON
Easy-ish formats { "production":{ "phpSettings":{ "display_startup_errors": false, "display_errors": false }, "includePaths":{ "library": "APPLICATION_PATH/../library" }, "bootstrap":{ "path": "APPLICATION_PATH/Bootstrap.php", "class": "Bootstrap" }, "appnamespace": "Application", }, "staging":{ "_extends": "production" } } INI Apache nginx JSON
Complex formats <configure xmlns="http://namespaces.zope.org/zope" xmlns:zmi="http://namespaces.zope.org/zmi" xmlns:browser="http://namespaces.zope.org/browser" > <browser:defaultView for="zope.i18n.interfaces.ITranslationService” name="index.html” /> <browser:view permission="zope.ManageServices" for="zope.i18n.interfaces.ITranslationService" factory="zope.app.browser.i18n.Translate" > <browser:pagename="index.html" attribute="index" /> </browser:view> <zmi:tabs for="zope.i18n.interfaces.ITranslationService"> <zmi:tablabel="Translate" action="@@index.html"/> </zmi:tabs> </configure> XML YAML
Complex formats application: myappversion: 1runtime: python27api_version: 1threadsafe: truehandlers:- url: /script: home.app- url: /index\.htmlscript: home.app- url: /stylesheetsstatic_dir: stylesheets- url: /(.*\.(gif|png|jpg))static_files: static/\1upload: static/(.*\.(gif|png|jpg)) XML YAML
Turing complete formats DEBUG = True TEMPLATE_DEBUG = DEBUG DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/tmp/project.db', 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', } } import os.path MODULE_ROOT = \ os.path.dirname(os.path.realpath(__file__)) STATIC_ROOT = os.sep.join((MODULE_ROOT, 'static')) STATIC_URL = '/static/’ Python
Awkward formats Feature: Addition In order to avoidmistakes As a mathslouch I want to be told the sum of twonumbers Scenario: Addtwonumbers Given I haveentered 50 into the calc And I haveentered 70 into the calc When I pressadd Then the resultshould be 120 Domain-Specific Languages SQLite Windows registry
Awkward formats classdavids_black_co_at { hosting::user { rztt: realname => "Gerhard Schmitt", uid => 2001, admin => true; conny: realname => "Conny Schmitt", uid => 2002; oma: realname => "Oma Schmitt", uid => 2003; } # Installgit.black.co.at include git::daemon include git::web git::web::export { [manifests, "puppet-trunk"]: } hosting::database { "fogbugz": type => mysql } apache2::site { "local-fogbugz": source => "puppet://$servername/files/hosting/davids/sites/local-fogbugz" } } Domain-Specific Languages SQLite Windows registry
Awkward formats # main.cf ... alias_maps = sqlite:/etc/postfix/sqlite-aliases.cf ... # sqliteconfig file for # local(8) aliases(5) lookups dbpath= /path/to/sqlite_database query= SELECT forw_addrFROM mxaliasesWHERE alias='%s’ AND status='paid' Domain-Specific Languages SQLite Windows registry
Awkward formats sqlite> .tables directory handler log proxyserverstatistic filter host mimetyperoutesetting sqlite> select * from host; 1|1|0|localhost|localhost sqlite> selectcount(*) from mimetype; 850 sqlite> select * from server; 1|AC1F8236-5919-9D40-0F38DE9E5861|/logs/access.log|/logs/error.log|./|/run/mongrel2.pid|localhost|test|0.0.0.0|6767|0 Domain-Specific Languages SQLite Windows registry
Awkward formats sqlite> .tables directory handler log proxyserverstatistic filter host mimetyperoutesetting sqlite> select * from host; 1|1|0|localhost|localhost sqlite> selectcount(*) from mimetype; 850 sqlite> select * from server; 1|AC1F8236-5919-9D40-0F38DE9E5861|/logs/access.log|/logs/error.log|./|/run/mongrel2.pid|localhost|test|0.0.0.0|6767|0 Domain-Specific Languages SQLite Windows registry #get s list of the availableservers to run m2sh servers -dbtests/config.sqlite# seewhathosts a serverhas m2sh hosts -dbtests/config.sqlite -server test # find out if a servernamed 'test' isrunning m2sh running -dbtests/config.sqlite -name test # start a serverwho'sdefault host is 'localhost' m2sh start -dbtests/config.sqlite -host localhost
Awkward formats Domain-Specific Languages SQLite Windows registry
The worst format ever Your own format
Seeking balance one-size-fits-all vs. tweakable-beyond-recognition
ambv@arrakis:~ $ wget-O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf - --2012-10-20 11:24:22-- https://www.dropbox.com/download?plat=lnx.x86_64 Resolving www.dropbox.com... 199.47.217.171, 199.47.216.170, 199.47.216.171, ... Connecting to www.dropbox.com|199.47.217.171|:443... connected. HTTP request sent, awaiting response... 302 FOUND Location: https://dl-web.dropbox.com/u/17/dropbox-lnx.x86_64-1.4.17.tar.gz [following] --2012-10-20 11:24:23-- https://dl-web.dropbox.com/u/17/dropbox-lnx.x86_64-1.4.17.tar.gz Resolving dl-web.dropbox.com... 174.129.197.250, 174.129.199.91, 107.20.174.220, ... Connecting to dl-web.dropbox.com|174.129.197.250|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 19090076 (18M) [application/x-tar] Saving to: `STDOUT' 100%[=============================================================>] 19,090,076 1.23M/s in 17s 2012-10-20 11:24:42 (1.08 MB/s) - `-' saved [19090076/19090076]
ambv@arrakis:~ $ wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf - 100%[======>] 19,090,076 1.23M/s in 17s ambv@arrakis:~ $ ./.dropbox-dist/dropboxd This client is not linked to any account... Please visit https://www.dropbox.com/cli_link?host_id=012345678&cl=en_US to link this machine.
Excerpts from >>> import this Beautifulisbetterthanugly. Simple isbetterthancomplex. Flat isbetterthannested. Readabilitycounts. Thereshould be one(and preferablyonlyone) obviousway to do it.
Hard coding • Ananti-pattern • Likehardwiringcircuits • Configurationembedded in sourcecode
Reverse hard coding • Source codeembedded in configuration • Alsoananti-pattern
Practical configurability DJANGo: settings.py
Djangosettings.py Djangomixesdifferentkinds of settings • Framework behaviour • Application behaviour • Deployment-specificconfiguration • databases • caches • logging • paths • ADMINS • MANAGERS • DEBUG • Sensitive data • passwords • SECURE_KEY The problem from settings import * Orderedincrementalexecfile django-configurations INI basedconfiguration
Djangosettings.py # settings.py # ... TIME_ZONE = 'Europe/Zurich' LANGUAGE_CODE = 'en-us' SECRET_KEY = 'secret' # ... from settings_local import * The problem from settings import * Orderedincrementalexecfile django-configurations INI basedconfiguration