Slide 1

Slide 1 text

λ-toolchain

Slide 2

Slide 2 text

λ-toolchain @darkproger (initially presented on Most Functional Day; Aug 9, 2014)

Slide 3

Slide 3 text

Nix purely functional package & configuration mgr programming language

Slide 4

Slide 4 text

Nix apt-get / yum json + macros puppet / chef {

Slide 5

Slide 5 text

Nix apt-get / yum json + macros puppet / chef

Slide 6

Slide 6 text

the language

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Nix (the expression language) lazy expression-oriented HOFs, closures

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

% nix-instantiate --eval -E 1 1

Slide 12

Slide 12 text

% nix-instantiate --eval -E ' builtins.trace "hello world" null ' trace: hello world null

Slide 13

Slide 13 text

% nix-instantiate --eval -E ' [ 1 2 3 ] ' [ 1 2 3 ] lists

Slide 14

Slide 14 text

% nix-instantiate --eval -E ' { hello = "world"; one = 1; } ' { hello = "world"; one = 1; } attribute sets

Slide 15

Slide 15 text

% nix-instantiate --eval -E ' rec { hello = "world"; again = hello; } ' { again = ; hello = "world"; } recursive attrsets

Slide 16

Slide 16 text

% nix-instantiate --eval --strict -E ' rec { hello = "world"; again = hello; } ' { again = "world"; hello = "world"; } (optional) strict evaluation

Slide 17

Slide 17 text

% nix-instantiate --eval -E ' (a: "hello ${a}") "john doe" ' "hello john doe" lambdas

Slide 18

Slide 18 text

% nix-instantiate --eval -E ' ((a: b: "hello ${a} ${b}") "john") ' currying

Slide 19

Slide 19 text

% nix-instantiate --eval -E ' (((a: b: "hello ${a} ${b}") "john") "doe") ' "hello john doe" currying

Slide 20

Slide 20 text

% nix-repl Welcome to Nix version 1.7. Type :? for help. nix-repl> :p let a = "one"; b = "two"; f = { foo, bar }: "${foo}: ${bar}”; in f { foo = a; bar = b; } "one: two" argument deconstruction

Slide 21

Slide 21 text

nix-repl> :p let foo = “one”; bar = “two"; f = { foo, bar }: "${foo}: ${bar}”; in f { inherit foo bar; } "one: two" scope inheritance

Slide 22

Slide 22 text

expression files

Slide 23

Slide 23 text

{ lib, ... }: with lib; let ec2 = { "my-elastic-ip" = "54.127.128.129/32"; }; rackspace = { "huge-box" = "154.24.23.52/32"; "build-box" = "165.123.32.23/32"; }; locations = { "home" = "127.0.0.1/32"; "office" = "169.254.169.254/32"; }; in rec { netmap = ec2 // rackspace // locations; whitelist = attrValues netmap; }

Slide 24

Slide 24 text

{ lib, ... }: with lib; let ec2 = { "my-elastic-ip" = "54.127.128.129/32"; }; rackspace = { "huge-box" = "154.24.23.52/32"; "build-box" = "165.123.32.23/32"; }; locations = { "home" = "127.0.0.1/32"; "office" = "169.254.169.254/32"; }; in rec { netmap = ec2 // rackspace // locations; whitelist = attrValues netmap; }

Slide 25

Slide 25 text

% nix-instantiate --eval my-networks.nix

Slide 26

Slide 26 text

% nix-instantiate --eval my-networks.nix { lib, ... }: with lib;

Slide 27

Slide 27 text

% nix-instantiate --eval \ --arg lib '(import {}).lib' \ my-networks.nix { netmap = ; whitelist = ; }

Slide 28

Slide 28 text

% nix-instantiate --eval --strict \ --arg lib '(import {}).lib' \ my-networks.nix { netmap = { build-box = "165.123.32.23/32"; home = "127.0.0.1/32"; huge-box = "154.24.23.52/32"; my-elastic-ip = "54.127.128.129/32"; office = "169.254.169.254/32"; }; whitelist = [ "165.123.32.23/32" "127.0.0.1/32" "154.24.23.52/32" "54.127.128.129/32" "169.254.169.254/32" ]; }

Slide 29

Slide 29 text

% nix-instantiate --eval --strict -E ' builtins.toJSON import ./my-networks.nix { inherit (import {}) lib; } ' error: cannot convert a partially applied built- in function to JSON

Slide 30

Slide 30 text

% nix-instantiate --eval --strict -E ' builtins.toJSON (import ./my-networks.nix { inherit (import {}) lib; }) ' | jq -r . | jq . { "whitelist": [ "165.123.32.23/32", "127.0.0.1/32", "154.24.23.52/32", "54.127.128.129/32", "169.254.169.254/32" ], "netmap": { "office": "169.254.169.254/32", "my-elastic-ip": "54.127.128.129/32", "huge-box": "154.24.23.52/32", "home": "127.0.0.1/32", "build-box": "165.123.32.23/32" } }

Slide 31

Slide 31 text

'' #!${pkgs.bash}/bin/bash # this string is going to be indented cd ${if cfg.dataDir != "" then cfg.dataDir else "/var/empty"} exec ${cfg.command} ''; smart interpolation

Slide 32

Slide 32 text

'' #!${pkgs.bash}/bin/bash # this string is going to be indented cd ${if cfg.dataDir != "" then cfg.dataDir else "/var/empty"} exec ${cfg.command} ''; "antiquotation"

Slide 33

Slide 33 text

derivations

Slide 34

Slide 34 text

let system = builtins.currentSystem; builder = /bin/bash; in rec { writeText = name: text: derivation { inherit system builder name; args = ["-c" "echo -n '${text}' > $out"]; }; }

Slide 35

Slide 35 text

% nix-instantiate -E ' (import ./ourlib.nix).writeText "hello" "world" ' warning: you did not specify `--add-root'; the result might be removed by the garbage collector /nix/store/ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv

Slide 36

Slide 36 text

% nix-instantiate -E ' (import ./ourlib.nix).writeText "hello" "world" ' warning: you did not specify `--add-root'; the result might be removed by the garbage collector /nix/store/ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv the store

Slide 37

Slide 37 text

% nix-instantiate -E ' (import ./ourlib.nix).writeText "hello" "world" ' warning: you did not specify `--add-root'; the result might be removed by the garbage collector /nix/store/ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv % nix-instantiate -E ' (import ./ourlib.nix).writeText "hello" "world" ' warning: you did not specify `--add-root'; the result might be removed by the garbage collector /nix/store/ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv

Slide 38

Slide 38 text

Derive( [("out","/nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello","","")], [], ["/nix/store/mv24aqvq44jvdxnhszx6kyps9jlf0ial-bash"], "x86_64-darwin", "/nix/store/mv24aqvq44jvdxnhszx6kyps9jlf0ial-bash", ["-c","echo -n 'world' > $out"], [("builder","/nix/store/mv24aqvq44jvdxnhszx6kyps9jlf0ial-bash"), ("name","hello"), ("out","/nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello"), ("system","x86_64-darwin")] ) "annotated term"

Slide 39

Slide 39 text

% nix-store --realise /nix/store/ ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv these derivations will be built: /nix/store/ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv building path(s) `/nix/store/ p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello' warning: you did not specify `--add-root'; the result might be removed by the garbage collector /nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello realising the derivation

Slide 40

Slide 40 text

% nix-store --realise /nix/store/ ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv warning: you did not specify `--add-root'; the result might be removed by the garbage collector /nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello ... only once

Slide 41

Slide 41 text

% nix-store --print-env /nix/store/ ic243k5dw29knvgp60x8nh22kmkbg4dh-hello.drv export builder; builder='/nix/store/mv24aqvq44jvdxnhszx6kyps9jlf0ial-bash' export name; name='hello' export out; out='/nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello' export system; system='x86_64-darwin' export _args; _args='-c echo -n '\''world'\'' > $out' reproducible env

Slide 42

Slide 42 text

% nix-shell --pure -E ' (import ./ourlib.nix).writeText "hello" "world" ' [nix-shell:~]$ nix-shell

Slide 43

Slide 43 text

% cat /nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello world

Slide 44

Slide 44 text

% cat /nix/store/p30mrskrnk8y5zvknc7ccppz3m30vkbf-hello world hash of all inputs

Slide 45

Slide 45 text

derivations package manager

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

% ll $(nix-instantiate --eval -E '')/ total 24 -rw-r--r-- 1 proger staff 1.6K Jun 17 13:36 COPYING -rw-r--r-- 1 proger staff 677B Jun 23 17:17 README.md -rw-r--r-- 1 proger staff 213B Jun 17 13:36 default.nix drwxr-xr-x 15 proger staff 510B Jul 21 10:17 doc/ drwxr-xr-x 22 proger staff 748B Jul 21 10:17 lib/ drwxr-xr-x 3 proger staff 102B Jun 17 13:36 maintainers/ drwxr-xr-x 13 proger staff 442B Jul 21 10:17 nixos/ drwxr-xr-x 16 proger staff 544B Jun 17 13:36 pkgs/

Slide 48

Slide 48 text

% cat ~dev/nix/nixpkgs/pkgs/os-specific/linux/latencytop/default.nix { stdenv, fetchurl, ncurses, glib, pkgconfig, gtk }: stdenv.mkDerivation rec { name = "latencytop-0.5"; patchPhase = "sed -i s,/usr,$out, Makefile"; preInstall = "mkdir -p $out/sbin"; src = fetchurl { urls = [ "http://latencytop.org/download/${name}.tar.gz" "http://dbg.download.sourcemage.org/mirror/latencytop-0.5.tar.gz" ]; sha256 = "1vq3j9zdab6njly2wp900b3d5244mnxfm88j2bkiinbvxbxp4zwy"; }; buildInputs = [ ncurses glib pkgconfig gtk ]; meta = { homepage = http://latencytop.org; description = "Tool to show kernel reports on latencies (LATENCYTOP option)"; license = "GPLv2"; maintainers = [ stdenv.lib.maintainers.viric ]; platforms = stdenv.lib.platforms.linux; }; }

Slide 49

Slide 49 text

stdenv basic toolchain for C ./configure --prefix=$out make make install (and more) unix idiosyncrasies unix flavours monkey-patching (patchelf) nix-shell matters even more

Slide 50

Slide 50 text

stdenv for $LANGUAGE build on top of language- specific package manager integrate with the rest of the world

Slide 51

Slide 51 text

% nix-store -qR /nix/store/ lfmx59j4ds9k7f6grs6l5y7y7wq52d3n-sysdig-0.1.82 /nix/store/x52mfsvjaz1k82iy8hh5bmh1jhz62kwg-gmp-5.1.3 /nix/store/q4dp57r561pqps48sqnps74xwz9fg9b4-isl-0.11.1 /nix/store/939lir5rs9axn2pch7sxgxwpvk2yq20d-cloog-0.18.0 /nix/store/fz99gjn0vi6nmxnv2rr6kh2sfc85mpy2-zlib-1.2.8 /nix/store/j81x6cd2zdqp1f0yprghgd266d5zgjx0-mpfr-3.1.2 /nix/store/jqmfwcky6r6m177ak87rsrfg825z383w-zlib-1.2.8 /nix/store/m551ya45l9gg0wclllhniszrccl3szb6-mpc-1.0.1 /nix/store/yy6px6c2fxmq4bcs6mzq16wfnlc9l36y-gcc-4.8.2 /nix/store/kif3h9a68a6vc3r7s7y0i3cszjjix21q-luajit-2.0.3 /nix/store/lfmx59j4ds9k7f6grs6l5y7y7wq52d3n-sysdig-0.1.82 store closure

Slide 52

Slide 52 text

% nix-copy-closure --to anotherbox /nix/store/ lfmx59j4ds9k7f6grs6l5y7y7wq52d3n-sysdig-0.1.82 copying 4 missing paths to ‘anotherbox’... importing path `/nix/store/fz99gjn0vi6nmxnv2rr6kh2sfc85mpy2-zlib-1.2.8' load: 1.23 cmd: ssh 24006 waiting 0.00u 0.00s load: 1.23 cmd: ssh 24006 waiting 0.00u 0.00s importing path `/nix/store/yy6px6c2fxmq4bcs6mzq16wfnlc9l36y-gcc-4.8.2' importing path `/nix/store/kif3h9a68a6vc3r7s7y0i3cszjjix21q-luajit-2.0.3' importing path `/nix/store/lfmx59j4ds9k7f6grs6l5y7y7wq52d3n-sysdig-0.1.82'

Slide 53

Slide 53 text

More distributed builds (fanout through ssh) caching for free

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

NixOS binary channels user environments (profiles) /etc/nixos/configuration.nix generations / rollbacks Solaris-like "boot configurations" mostly designed for laptops / legacy systems :(

Slide 56

Slide 56 text

configuration management

Slide 57

Slide 57 text

misconfiguration management

Slide 58

Slide 58 text

configuration mismanagement

Slide 59

Slide 59 text

puppet chef / ansible / salt / whatever

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

let region = "ap-southeast-1"; accessKeyId = "devops"; gen-instance = { resources, ... }: (import ./thumbor.nix {}) { deployment.ec2 = { inherit accessKeyId region; instanceType = "c3.2xlarge"; }; fileSystems."/data" = { ec2.disk = "ephemeral0"; }; }; in { resources.ec2KeyPairs.default = { inherit accessKeyId region; }; thumbor1 = gen-instance; thumbor2 = gen-instance; thumbor3 = gen-instance; thumbor4 = gen-instance; }

Slide 63

Slide 63 text

????

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

{ lib }: with lib; rec { mapcat = f: x: concatLists (mapAttrsToList f x); mapcata = f: x: listToAttrs (mapcat f x); } maps/filters included

Slide 66

Slide 66 text

resources.elbs.web = { resources, config, pkgs, lib, ... }: with lib; { inherit region accessKeyId; name = "${name}-web"; subnets = [ resources.subnets.default ]; securityGroups = [ resources.ec2SecurityGroups.default ]; machines = mapAttrsToList (name: m: m) resources.machines; route53Aliases = mapcata (_name: app: (map (prefix: { name = prefix + app.dns.public.name; value = { zoneId = app.dns.public.zoneId; }; }) ["" "*."])) all-apps; };

Slide 67

Slide 67 text

Integration --eval --strict --xml builtins.toJSON builtins.fromJSON staged evaluations perl bindings (omg please don't)

Slide 68

Slide 68 text

Future hnix https:/ /github.com/jwiegley/hnix

Slide 69

Slide 69 text

More nix-docker cabal2nix python2nix hydra (Nix CI) guix smart community!

Slide 70

Slide 70 text

Where it helps (aka show me the money)

Slide 71

Slide 71 text

reconciling configuration hell (tooling helps)

Slide 72

Slide 72 text

separating implementation and declaration while keeping code and configuration together

Slide 73

Slide 73 text

managing dependency hell (e.g. sudden rewrites of services from Go to Python)

Slide 74

Slide 74 text

bridging multilang dependencies (i.e. Python with C with Haskell)

Slide 75

Slide 75 text

writing shell scripts!

Slide 76

Slide 76 text

VPC IP<->Route53 mapping https://gist.github.com/proger/49e7ae550f4e2d4c99af

Slide 77

Slide 77 text

zone=ZOZONEZONEZONE date=$(curl -s -I https://route53.amazonaws.com/date \ | awk '/^Date: / { sub("Date: ", "", $0); sub("\\r", "", $0); print $0 }') set -- $(curl -s http://169.254.169.254/latest/meta-data/ iam/security-credentials/dns \ | jq -r '.SecretAccessKey, .AccessKeyId, .Token') signature=$(echo -n $date \ | openssl dgst -binary -sha1 -hmac $1 \ | base64) auth_header="X-Amzn-Authorization: AWS3-HTTPS AWSAccessKeyId=$2,Algorithm=HmacSHA1,Signature=$signature" hostname=$(hostname).doge-networking-enterprises.com local_hostname=$(curl -s http://169.254.169.254/latest/ meta-data/local-hostname)

Slide 78

Slide 78 text

zone date | awk sub("Date: ", "", $0); sub("\\r", "", $0); print $0 }' set iam/security-credentials/dns | jq -r signature | openssl dgst -binary -sha1 -hmac | base64 auth_header AWSAccessKeyId=$2,Algorithm=HmacSHA1,Signature=$signature" hostname local_hostname meta-data/local-hostname curl awk curl jq openssl coreutils curl bash

Slide 79

Slide 79 text

bash = "${pkgs.bash}/bin/bash"; base64 = "${pkgs.coreutils}/bin/base64"; jq = "${pkgs.jq}/bin/jq"; curl = "${pkgs.curl}/bin/curl -s --retry --fail"; awk = "${pkgs.gawk}/bin/awk"; openssl = "${pkgs.openssl}/bin/openssl";

Slide 80

Slide 80 text

zone date | awk sub("Date: ", "", $0); sub("\\r", "", $0); print $0 }' set iam/security-credentials/dns | jq -r signature | openssl dgst -binary -sha1 -hmac | base64 auth_header AWSAccessKeyId=$2,Algorithm=HmacSHA1,Signature=$signature" hostname local_hostname meta-data/local-hostname ${curl} ${awk} ${jq} ${openssl} ${base64} ${curl} ${curl} ${bash}

Slide 81

Slide 81 text

speeding up Haskell development (benefits of cabal sandbox without its disadvantages)

Slide 82

Slide 82 text

managing Cabal hell & GHC versions

Slide 83

Slide 83 text

library isolation for C and C++ software (that's usually where containers shine, but works for us)

Slide 84

Slide 84 text

teamwork & pull requests

Slide 85

Slide 85 text

developer environments close to production

Slide 86

Slide 86 text

lifting your legacy apps into the cloud (your apps don't need configuration, remember?)

Slide 87

Slide 87 text

Getting started mix and match Nix with Docker Nix on Debian with Chef Nix on Debian with NixOps

Slide 88

Slide 88 text

https:/ /github.com/zalora we're using and improving Nix

Slide 89

Slide 89 text

stay tuned ask me anything @darkproger