1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.virtualisation.amazon-init;
7
8 script = ''
9 #!${pkgs.runtimeShell} -eu
10
11 echo "attempting to fetch configuration from EC2 user data..."
12
13 export HOME=/root
14 export PATH=${pkgs.lib.makeBinPath [ config.nix.package config.systemd.package pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused pkgs.xz config.system.build.nixos-rebuild]}:$PATH
15 export NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels
16
17 userData=/etc/ec2-metadata/user-data
18
19 # Check if user-data looks like a shell script and execute it with the
20 # runtime shell if it does. Otherwise treat it as a nixos configuration
21 # expression
22 if IFS= LC_ALL=C read -rN2 shebang < $userData && [ "$shebang" = '#!' ]; then
23 # NB: we cannot chmod the $userData file, this is why we execute it via
24 # `pkgs.runtimeShell`. This means we have only limited support for shell
25 # scripts compatible with the `pkgs.runtimeShell`.
26 exec ${pkgs.runtimeShell} $userData
27 fi
28
29 if [ -s "$userData" ]; then
30 # If the user-data looks like it could be a nix expression,
31 # copy it over. Also, look for a magic three-hash comment and set
32 # that as the channel.
33 if sed '/^\(#\|SSH_HOST_.*\)/d' < "$userData" | grep -q '\S'; then
34 channels="$(grep '^###' "$userData" | sed 's|###\s*||')"
35 while IFS= read -r channel; do
36 echo "writing channel: $channel"
37 done < <(printf "%s\n" "$channels")
38
39 if [[ -n "$channels" ]]; then
40 printf "%s" "$channels" > /root/.nix-channels
41 nix-channel --update
42 fi
43
44 echo "setting configuration from EC2 user data"
45 cp "$userData" /etc/nixos/configuration.nix
46 else
47 echo "user data does not appear to be a Nix expression; ignoring"
48 exit
49 fi
50 else
51 echo "no user data is available"
52 exit
53 fi
54
55 nixos-rebuild switch
56 '';
57in {
58
59 options.virtualisation.amazon-init = {
60 enable = mkOption {
61 default = true;
62 type = types.bool;
63 description = ''
64 Enable or disable the amazon-init service.
65 '';
66 };
67 };
68
69 config = mkIf cfg.enable {
70 systemd.services.amazon-init = {
71 inherit script;
72 description = "Reconfigure the system from EC2 userdata on startup";
73
74 wantedBy = [ "multi-user.target" ];
75 after = [ "multi-user.target" ];
76 requires = [ "network-online.target" ];
77
78 restartIfChanged = false;
79 unitConfig.X-StopOnRemoval = false;
80
81 serviceConfig = {
82 Type = "oneshot";
83 RemainAfterExit = true;
84 };
85 };
86 };
87 meta.maintainers = with maintainers; [ arianvp ];
88}