diff options
Diffstat (limited to 'krebs/3modules')
-rw-r--r-- | krebs/3modules/build.nix | 72 | ||||
-rw-r--r-- | krebs/3modules/build/default.nix | 269 | ||||
-rw-r--r-- | krebs/3modules/build/infest/finalize.sh | 65 | ||||
-rw-r--r-- | krebs/3modules/build/infest/install-nix.sh | 57 | ||||
-rw-r--r-- | krebs/3modules/build/infest/prepare.sh | 74 | ||||
-rw-r--r-- | krebs/3modules/default.nix | 126 |
6 files changed, 185 insertions, 478 deletions
diff --git a/krebs/3modules/build.nix b/krebs/3modules/build.nix new file mode 100644 index 000000000..57495ea69 --- /dev/null +++ b/krebs/3modules/build.nix @@ -0,0 +1,72 @@ +{ config, lib, ... }: + +with import ../4lib { inherit lib; }; + +let + target = config.krebs.build // { user.name = "root"; }; + + out = { + # TODO deprecate krebs.build.host + options.krebs.build.host = mkOption { + type = types.host; + }; + + # TODO make krebs.build.profile shell safe + options.krebs.build.profile = mkOption { + type = types.str; + default = "/nix/var/nix/profiles/system"; + }; + + # TODO make krebs.build.target.host :: host + options.krebs.build.target = mkOption { + type = with types; nullOr str; + default = null; + }; + + # TODO deprecate krebs.build.user + options.krebs.build.user = mkOption { + type = types.user; + }; + + options.krebs.build.source.dir = mkOption { + type = types.attrsOf (types.submodule ({ config, ... }: { + options = { + host = mkOption { + type = types.host; + }; + path = mkOption { + type = types.str; + }; + target-path = mkOption { + type = types.str; + default = "/root/${config._module.args.name}"; + }; + url = mkOption { + type = types.str; + default = "file://${config.host.name}${config.path}"; + }; + }; + })); + default = {}; + }; + + options.krebs.build.source.git = mkOption { + type = with types; attrsOf (submodule ({ config, ... }: { + options = { + url = mkOption { + type = types.str; # TODO must be shell safe + }; + rev = mkOption { + type = types.str; + }; + target-path = mkOption { + type = types.str; + default = "/root/${config._module.args.name}"; + }; + }; + })); + default = {}; + }; + }; + +in out diff --git a/krebs/3modules/build/default.nix b/krebs/3modules/build/default.nix deleted file mode 100644 index 4d2f36a02..000000000 --- a/krebs/3modules/build/default.nix +++ /dev/null @@ -1,269 +0,0 @@ -{ config, lib, ... }: - -with import ../../4lib { inherit lib; }; - -let - target = config.krebs.build // { user.name = "root"; }; - - out = { - # TODO deprecate krebs.build.host - options.krebs.build.host = mkOption { - type = types.host; - }; - - # TODO make krebs.build.profile shell safe - options.krebs.build.profile = mkOption { - type = types.str; - default = "/nix/var/nix/profiles/system"; - }; - - # TODO make krebs.build.target.host :: host - options.krebs.build.target = mkOption { - type = with types; nullOr str; - default = null; - }; - - # TODO deprecate krebs.build.user - options.krebs.build.user = mkOption { - type = types.user; - }; - - options.krebs.build.scripts.deploy = lib.mkOption { - type = lib.types.str; - default = '' - set -efu - (${config.krebs.build.scripts._source}) - ${ssh-target '' - ${config.krebs.build.scripts._nix-env} - ${config.krebs.build.profile}/bin/switch-to-configuration switch - ''} - echo OK - ''; - }; - - options.krebs.build.scripts.infest = lib.mkOption { - type = lib.types.str; - default = '' - set -efu - - export RSYNC_RSH; RSYNC_RSH="$(type -p ssh) \ - -o 'HostName ${target.host.infest.addr}' \ - -o 'Port ${toString target.host.infest.port}' \ - " - ssh() { - eval "$RSYNC_RSH \"\$@\"" - } - - ${ssh-target '' - ${readFile ./infest/prepare.sh} - ${readFile ./infest/install-nix.sh} - ''} - - (${config.krebs.build.scripts._source}) - - ${ssh-target '' - export PATH; PATH=/root/.nix-profile/bin:$PATH - - src=$(type -p nixos-install) - cat_src() { - sed < "$src" "$( - sed < "$src" -n ' - /^if ! test -e "\$mountPoint\/\$NIXOS_CONFIG/,/^fi$/= - /^nixpkgs=/= - /^NIX_PATH=/,/^$/{/./=} - ' \ - | sed 's:$:s/^/#krebs#/:' - )" - } - - # Location to insert config.krebs.build.scripts._nix-env - i=$(sed -n '/^echo "building the system configuration/=' "$src") - - { - cat_src | sed -n "1,$i{p}" - cat ${doc config.krebs.build.scripts._nix-env} - cat_src | sed -n "$i,\''${$i!p}" - } > nixos-install - chmod +x nixos-install - - # Wrap inserted config.krebs.build.scripts._nix-env into chroot. - nix_env=$(cat_src | sed -n ' - s:.*\(/nix/store/[a-z0-9]*-nix-[0-9.]\+/bin/nix-env\).*:\1:p;T;q - ') - echo nix-env is $nix_env - sed -i ' - s:^nix-env:chroot $mountPoint '"$nix_env"': - ' nixos-install - - ./nixos-install - - ${readFile ./infest/finalize.sh} - ''} - ''; - }; - - options.krebs.build.scripts._nix-env = lib.mkOption { - type = lib.types.str; - default = '' - set -efu - NIX_PATH=${config.krebs.build.source.NIX_PATH} \ - nix-env \ - -f '<stockholm>' \ - -Q \ - --argstr user-name ${config.krebs.exec.user.name} \ - --argstr host-name ${target.host.name} \ - --profile ${config.krebs.build.profile} \ - --set \ - -A ${lib.escapeShellArg (lib.concatStringsSep "." [ - config.krebs.build.user.name - config.krebs.build.host.name - "system" - ])} - ''; - }; - - options.krebs.build.scripts._source = lib.mkOption { - type = lib.types.str; - default = '' - set -efu - ${ - lib.concatStringsSep "\n" - (lib.mapAttrsToList - (name: { scripts, url, ... }: "(${scripts._source})") - (config.krebs.build.source.dir // - config.krebs.build.source.git)) - } - ''; - }; - - options.krebs.build.source.NIX_PATH = mkOption { - type = types.str; - default = - lib.concatStringsSep ":" - (lib.mapAttrsToList (name: _: "${name}=/root/${name}") - (config.krebs.build.source.dir // - config.krebs.build.source.git)); - }; - - options.krebs.build.source.dir = mkOption { - type = - let - exec = config.krebs.exec; - in - types.attrsOf (types.submodule ({ config, ... }: - let - url = "file://${config.host.name}${config.path}"; - - can-link = config.host.name == target.host.name; - can-push = config.host.name == exec.host.name; - - push-method = '' - rsync \ - --exclude .git \ - --exclude .graveyard \ - --exclude old \ - --exclude tmp \ - --rsync-path='mkdir -p ${config.target-path} && rsync' \ - --delete-excluded \ - -vrLptgoD \ - ${config.path}/ \ - ${target.user.name}@${target.host.name}:${config.target-path} - ''; - in - { - options = { - host = mkOption { - type = types.host; - description = '' - define the host where the directory is stored on. - XXX: currently it is just used to check if rsync is working, - becomes part of url - ''; - }; - path = mkOption { - type = types.str; - }; - scripts._source = mkOption { - type = types.str; - default = - #if can-link then link-method else - if can-push then push-method else - throw "cannot source ${url}"; - }; - target-path = mkOption { - type = types.str; - default = "/root/${config._module.args.name}"; - }; - url = mkOption { - type = types.str; - default = "file://${config.host.name}${config.path}"; - }; - }; - } - )); - default = {}; - }; - - options.krebs.build.source.git = mkOption { - type = - let - target = config.krebs.build // { user.name = "root"; }; - in - with types; attrsOf (submodule ({ config, ... }: - { - options = { - url = mkOption { - type = types.str; # TODO must be shell safe - }; - rev = mkOption { - type = types.str; - }; - scripts._source = mkOption { - type = types.str; - default = ssh-target '' - mkdir -p ${config.target-path} - cd ${config.target-path} - if ! test -e .git; then - git init - fi - if ! cur_url=$(git config remote.origin.url 2>/dev/null); then - git remote add origin ${config.url} - elif test "$cur_url" != ${config.url}; then - git remote set-url origin ${config.url} - fi - if test "$(git rev-parse --verify HEAD 2>/dev/null)" != ${config.rev}; then - git fetch origin - git checkout ${config.rev} -- . - git checkout -q ${config.rev} - git submodule init - git submodule update - fi - git clean -dxf - ''; - }; - target-path = mkOption { - type = types.str; - default = "/root/${config._module.args.name}"; - }; - }; - } - )); - default = {}; - }; - }; - - doc = s: - let b = "EOF${hashString "sha256" s}"; in - '' - <<\${b} - ${s} - ${b} - ''; - - ssh-target = script: - "ssh root@${target.host.name} -T ${doc '' - set -efu - ${script} - ''}"; - -in out diff --git a/krebs/3modules/build/infest/finalize.sh b/krebs/3modules/build/infest/finalize.sh deleted file mode 100644 index ced5a4d4d..000000000 --- a/krebs/3modules/build/infest/finalize.sh +++ /dev/null @@ -1,65 +0,0 @@ -#! /bin/sh -set -eux -{ - umount /mnt/nix || [ $? -eq 32 ] - umount /mnt/boot || [ $? -eq 32 ] - umount /mnt/root || [ $? -eq 32 ] - umount /mnt || [ $? -eq 32 ] - umount /boot || [ $? -eq 32 ] - - PATH=$(set +f; for i in /nix/store/*coreutils*/bin; do :; done; echo $i) - export PATH - - mkdir /oldshit - - mv /bin /oldshit/ - mv /newshit/bin / - - # TODO ensure /boot is empty - rmdir /newshit/boot - - # skip /dev - rmdir /newshit/dev - - mv /etc /oldshit/ - mv /newshit/etc / - - # skip /nix (it's already there) - rmdir /newshit/nix - - # skip /proc - rmdir /newshit/proc - - # skip /run - rmdir /newshit/run - - # skip /sys - rmdir /newshit/sys - - # skip /root - rmdir /newshit/root - - # skip /tmp - # TODO rmdir /newshit/tmp - - mv /home /oldshit/ - mv /newshit/home / - - mv /usr /oldshit/ - mv /newshit/usr / - - mv /var /oldshit/ - mv /newshit/var / - - mv /lib /oldshit/ - mv /lib64 /oldshit/ - mv /sbin /oldshit/ - mv /srv /oldshit/ - mv /opt /oldshit/ - - - mv /newshit /root/ # TODO this one shoult be empty - mv /oldshit /root/ - - sync -} diff --git a/krebs/3modules/build/infest/install-nix.sh b/krebs/3modules/build/infest/install-nix.sh deleted file mode 100644 index 88c8c3e1e..000000000 --- a/krebs/3modules/build/infest/install-nix.sh +++ /dev/null @@ -1,57 +0,0 @@ -#! /bin/sh -set -efu - -nix_url=https://nixos.org/releases/nix/nix-1.10/nix-1.10-x86_64-linux.tar.bz2 -nix_sha256=504f7a3a85fceffb8766ae5e1005de9e02e489742f5a63cc3e7552120b138bf4 - -install_nix() {( - - # install nix on host (cf. https://nixos.org/nix/install) - if ! test -e /root/.nix-profile/etc/profile.d/nix.sh; then - ( - verify() { - printf '%s %s\n' $nix_sha256 $(basename $nix_url) | sha256sum -c - } - if ! verify; then - curl -C - -O "$nix_url" - verify - fi - ) - nix_src_dir=$(basename $nix_url .tar.bz2) - tar jxf $nix_src_dir.tar.bz2 - mkdir -v -m 0755 -p /nix - $nix_src_dir/install - fi - - #TODO: make this general or move to prepare - if ! mount | grep -Fq '/dev/mapper/centos-root on /mnt/nix type xfs'; then - mkdir -p /mnt/nix - mount --bind /nix /mnt/nix - fi - - . /root/.nix-profile/etc/profile.d/nix.sh - - for i in \ - bash \ - coreutils \ - # This line intentionally left blank. - do - if ! nix-env -q $i | grep -q .; then - nix-env -iA nixpkgs.pkgs.$i - fi - done - - # install nixos-install - if ! type nixos-install 2>/dev/null; then - nixpkgs_expr='import <nixpkgs> { system = builtins.currentSystem; }' - nixpkgs_path=$(find /nix/store -mindepth 1 -maxdepth 1 -name *-nixpkgs-* -type d) - nix-env \ - --arg config "{ nix.package = ($nixpkgs_expr).nix; }" \ - --arg pkgs "$nixpkgs_expr" \ - --arg modulesPath 'throw "no modulesPath"' \ - -f $nixpkgs_path/nixpkgs/nixos/modules/installer/tools/tools.nix \ - -iA config.system.build.nixos-install - fi -)} - -install_nix "$@" diff --git a/krebs/3modules/build/infest/prepare.sh b/krebs/3modules/build/infest/prepare.sh deleted file mode 100644 index 07c00c3a5..000000000 --- a/krebs/3modules/build/infest/prepare.sh +++ /dev/null @@ -1,74 +0,0 @@ -#! /bin/sh -set -efu - -prepare() {( - if test -e /etc/os-release; then - . /etc/os-release - case $ID in - centos) - case $VERSION_ID in - 7) - prepare_centos7 "$@" - exit - ;; - esac - ;; - esac - fi - echo "$0 prepare: unknown OS" >&2 - exit -1 -)} - -prepare_centos7() { - type bzip2 2>/dev/null || yum install -y bzip2 - type git 2>/dev/null || yum install -y git - type rsync 2>/dev/null || yum install -y rsync - if ! getent group nixbld >/dev/null; then - groupadd -g 30000 -r nixbld - fi - for i in `seq 1 10`; do - if ! getent passwd nixbld$i 2>/dev/null; then - useradd \ - -c "CentOS Nix build user $i" \ - -d /var/empty \ - -g 30000 \ - -G 30000 \ - -l \ - -M \ - -s /sbin/nologin \ - -u $(expr 30000 + $i) \ - nixbld$i - rm -f /var/spool/mail/nixbld$i - fi - done - - # - # mount install directory - # - - if ! mount | grep -Fq '/dev/mapper/centos-root on /mnt type xfs'; then - mkdir -p /newshit - mount --bind /newshit /mnt - fi - - if ! mount | grep -Fq '/dev/sda1 on /mnt/boot type xfs'; then - mkdir -p /mnt/boot - mount /dev/sda1 /mnt/boot - fi - - mount | grep 'on /mnt\>' >&2 - - # - # prepare install directory - # - - mkdir -p /mnt/etc/nixos - mkdir -m 0555 -p /mnt/var/empty - - if ! mount | grep -Fq '/dev/mapper/centos-root on /mnt/root type xfs'; then - mkdir -p /mnt/root - mount --bind /root /mnt/root - fi -} - -prepare "$@" diff --git a/krebs/3modules/default.nix b/krebs/3modules/default.nix index dc30b9c50..2d3b7b077 100644 --- a/krebs/3modules/default.nix +++ b/krebs/3modules/default.nix @@ -6,7 +6,7 @@ let out = { imports = [ - ./build + ./build.nix ./exim-retiolum.nix ./exim-smarthost.nix ./github-hosts-sync.nix @@ -84,13 +84,16 @@ let mapAttrsToList (hostname: host: mapAttrsToList (netname: net: let - aliases = toString (unique (longs ++ shorts)); + aliases = longs ++ shorts; providers = dns.split-by-provider net.aliases cfg.dns.providers; longs = providers.hosts; - shorts = map (removeSuffix ".${cfg.search-domain}") longs; + shorts = + map (removeSuffix ".${cfg.search-domain}") + (filter (hasSuffix ".${cfg.search-domain}") + longs); in - map (addr: "${addr} ${aliases}") net.addrs - ) host.nets + map (addr: "${addr} ${toString aliases}") net.addrs + ) (filterAttrs (name: host: host.aliases != []) host.nets) ) cfg.hosts )); @@ -100,6 +103,36 @@ let ([cfg.zone-head-config] ++ combined-hosts) ; combined-hosts = (mapAttrsToList (name: value: value.extraZones) cfg.hosts ); in lib.mapAttrs' (name: value: nameValuePair (("zones/" + name)) ({ text=value; })) all-zones; + + services.openssh.hostKeys = + let inherit (config.krebs.build.host.ssh) privkey; in + mkIf (privkey != null) (mkForce [privkey]); + + services.openssh.knownHosts = + mapAttrs + (name: host: { + hostNames = + concatLists + (mapAttrsToList + (net-name: net: + let + aliases = shorts ++ longs; + longs = net.aliases; + shorts = + map (removeSuffix ".${cfg.search-domain}") + (filter (hasSuffix ".${cfg.search-domain}") + longs); + add-port = a: + if net.ssh.port != null + then "[${a}]:${toString net.ssh.port}" + else a; + in + aliases ++ map add-port net.addrs) + host.nets); + + publicKey = host.ssh.pubkey; + }) + (filterAttrs (_: host: host.ssh.pubkey != null) cfg.hosts); } ]; @@ -110,7 +143,7 @@ let dc = "lass"; #dc = "cac"; nets = rec { internet = { - addrs4 = ["162.248.8.63"]; + addrs4 = ["104.233.84.57"]; aliases = [ "echelon.internet" ]; @@ -125,12 +158,42 @@ let ]; tinc.pubkey = '' -----BEGIN RSA PUBLIC KEY----- - MIIBCgKCAQEA92ybhDahtGybpAkUNlG5Elxw05MVY4Pg7yK0dQugB4nVq+pnmi78 - DOMeIciecMHmJM8n9UlUU0eWZVCgHeVd23d6J0hTHCv24p24uHEGGy7XlO/dPJ6A - IjROYU0l8c03pipdJ3cDBx6riArSglwmZJ7xH/Iw0BUhRZrPqbtijY7EcG2wc+8K - N9N9mBofVMl4EcBiDR/eecK+ro8OkeOmYPtYgFJLvxTYXiPIhOxMAlkOY2fpin/t - cgFLUFuN4ag751XjjcNpVovVq95vdg+VhKrrNVWZjJt03owW81BzoryY6CD2kIPq - UxK89zEdeYOUT7AxaT/5V5v41IvGFZxCzwIDAQAB + MIIBCgKCAQEAuscWOYdHu0bpWacvwTNd6bcmrAQ0YFxJWHZF8kPZr+bMKIhnXLkJ + oJheENIM6CA9lQQQFUxh2P2pxZavW5rgVlJxIKeiB+MB4v6ZO60LmZgpCsWGD/dX + MipM2tLtQxYhvLJIJxEBWn3rxIgeEnCtZsH1KLWyLczb+QpvTjMJ4TNh1nEBPE/f + 4LUH1JHaGhcaHl2dLemR9wnnDIjmSj0ENJp2al+hWnIggcA/Zp0e4b86Oqbbs5wA + n++n5j971cTrBdA89nJDYOEtepisglScVRbgLqJG81lDA+n24RWFynn+U3oD/L8p + do+kxlwZUEDRbPU4AO5L+UeIbimsuIfXiQIDAQAB + -----END RSA PUBLIC KEY----- + ''; + }; + }; + }; + fastpoke = { + dc = "lass"; #dc = "cac"; + nets = rec { + internet = { + addrs4 = ["193.22.164.36"]; + aliases = [ + "fastpoke.internet" + ]; + }; + retiolum = { + via = internet; + addrs4 = ["10.243.253.152"]; + addrs6 = ["42:422a:194f:ff3b:e196:2f82:5cf5:bc00"]; + aliases = [ + "fastpoke.retiolum" + "cgit.fastpoke.retiolum" + ]; + tinc.pubkey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEAs4p5xsQYx06v+OkUbc09K6voFAbkvO66QdFoM71E10XyCeLP6iuq + DaIOFN4GrPR36pgyjqtJ+62G9uR+WsB/y14eio1p1ivDWgcpt5soOZAH5zVRRD9O + FBDlgVNwIJ6stMHy6OenEKWsfEiZRN3XstnqAqyykzjddglth1tJntn6kbZehzNQ + ezfIyN4XgaX2fhSu+UnAyLcV8wWnF9cMABjz7eKcSmRJgtG4ZiuDkbgiiEew7+pB + EPqOVQ80lJvzQKgO4PmVoAjD9A+AHnmLJNPDQQi8nIVilGCT60IX+XT1rt85Zpdy + rEaeriw/qsVJnberAhDAdQYYuM1ai2H5swIDAQAB -----END RSA PUBLIC KEY----- ''; }; @@ -501,6 +564,7 @@ let "cgit.cd.viljetic.de" "cd.krebsco.de" ]; + ssh.port = 11423; }; retiolum = { via = internet; @@ -527,6 +591,8 @@ let ''; }; }; + ssh.privkey.path = <secrets/ssh.id_ed25519>; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOd/HqZIO9Trn3eycl23GZAz21HQCISaVNfNyaLSQvJ6"; }; mkdir = rec { cores = 1; @@ -534,7 +600,7 @@ let infest.addr = head nets.internet.addrs4; nets = rec { internet = { - addrs4 = ["104.233.84.102"]; + addrs4 = ["104.233.84.215"]; aliases = [ "mkdir.internet" ]; @@ -559,6 +625,35 @@ let ''; }; }; + ssh.privkey.path = <secrets/ssh.id_ed25519>; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuShEqU0Cdm7KCaMD5x1D6mgj+cr7qoqbzFJDKoBbbw"; + }; + ire = { + nets = { + internet = { + addrs4 = ["198.147.22.115"]; + ssh.port = 11423; + }; + retiolum = { + addrs4 = ["10.243.231.66"]; + addrs6 = ["42:b912:0f42:a82d:0d27:8610:e89b:490c"]; + aliases = [ + "ire.retiolum" + ]; + tinc.pubkey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEAwofjmP/XBf5pwsJlWklkSzI+Bo0I0B9ONc7/j+zpbmMRkwbWk4X7 + rVLt1cWvTY15ujg2u8l0o6OgEbIkc6rslkD603fv1sEAd0KOv7iKLgRpE9qfSvAt + 6YpiSv+mxEMTpH0g36OmBfOJ10uT+iHDB/FfxmgGJx//jdJADzLjjWC6ID+iGkGU + 1Sf+yHXF7HRmQ29Yak8LYVCJpGC5bQfWIMSL5lujLq4NchY2d+NZDkuvh42Ayr0K + LPflnPBQ3XnKHKtSsnFR2vaP6q+d3Opsq/kzBnAkjL26jEuFK1v7P/HhNhJoPzwu + nKKWj/W/k448ce374k5ycjvKm0c6baAC/wIDAQAB + -----END RSA PUBLIC KEY----- + ''; + ssh.port = 11423; + }; + }; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBaMjBJ/BfYlHjyn5CO0xzFNaQ0LPvMP3W9UlOs1OxGY"; }; nomic = { cores = 2; @@ -584,6 +679,7 @@ let }; }; secure = true; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILn7C3LxAs9kUynENdRNgQs4qjrhNDfXzlHTpVJt6e09"; }; rmdir = rec { cores = 1; @@ -616,6 +712,8 @@ let ''; }; }; + ssh.privkey.path = <secrets/ssh.id_ed25519>; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICLuhLRmt8M5s2Edwwl9XY0KAAivzmPCEweesH5/KhR4"; }; wu = { cores = 4; @@ -641,6 +739,7 @@ let }; }; secure = true; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIcJvu8JDVzObLUtlAQg9qVugthKSfitwCljuJ5liyHa"; }; xu = { cores = 4; @@ -666,6 +765,7 @@ let }; }; secure = true; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID554niVFWomJjuSuQoiCdMUYrCFPpPzQuaoXXYYDxlw"; }; }; users = addNames { |