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