1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 inherit (lib)
10 mkOption
11 optionalString
12 types
13 versionAtLeast
14 ;
15 inherit (lib.options) literalExpression;
16 cfg = config.amazonImage;
17 amiBootMode = if config.ec2.efi then "uefi" else "legacy-bios";
18in
19{
20 imports = [
21 ../../../modules/virtualisation/amazon-image.nix
22 ../../../modules/virtualisation/disk-size-option.nix
23 ../../../modules/image/file-options.nix
24 (lib.mkRenamedOptionModuleWith {
25 sinceRelease = 2411;
26 from = [
27 "amazonImage"
28 "sizeMB"
29 ];
30 to = [
31 "virtualisation"
32 "diskSize"
33 ];
34 })
35 (lib.mkRenamedOptionModuleWith {
36 sinceRelease = 2505;
37 from = [
38 "amazonImage"
39 "name"
40 ];
41 to = [
42 "image"
43 "baseName"
44 ];
45 })
46 ];
47
48 # Amazon recommends setting this to the highest possible value for a good EBS
49 # experience, which prior to 4.15 was 255.
50 # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html#timeout-nvme-ebs-volumes
51 config.boot.kernelParams =
52 let
53 timeout =
54 if versionAtLeast config.boot.kernelPackages.kernel.version "4.15" then "4294967295" else "255";
55 in
56 [ "nvme_core.io_timeout=${timeout}" ];
57
58 options.amazonImage = {
59 contents = mkOption {
60 example = literalExpression ''
61 [ { source = pkgs.memtest86 + "/memtest.bin";
62 target = "boot/memtest.bin";
63 }
64 ]
65 '';
66 default = [ ];
67 description = ''
68 This option lists files to be copied to fixed locations in the
69 generated image. Glob patterns work.
70 '';
71 };
72
73 format = mkOption {
74 type = types.enum [
75 "raw"
76 "qcow2"
77 "vpc"
78 ];
79 default = "vpc";
80 description = "The image format to output";
81 };
82 };
83
84 # Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
85 # to avoid breaking existing configs using that.
86 config.virtualisation.diskSize = lib.mkOverride 1490 (4 * 1024);
87 config.virtualisation.diskSizeAutoSupported = !config.ec2.zfs.enable;
88
89 config.system.nixos.tags = [ "amazon" ];
90 config.system.build.image = config.system.build.amazonImage;
91 config.image.extension = if cfg.format == "vpc" then "vhd" else cfg.format;
92
93 config.system.build.amazonImage =
94 let
95 configFile = pkgs.writeText "configuration.nix" ''
96 { modulesPath, ... }: {
97 imports = [ "''${modulesPath}/virtualisation/amazon-image.nix" ];
98 ${optionalString config.ec2.efi ''
99 ec2.efi = true;
100 ''}
101 ${optionalString config.ec2.zfs.enable ''
102 ec2.zfs.enable = true;
103 networking.hostId = "${config.networking.hostId}";
104 ''}
105 }
106 '';
107
108 zfsBuilder = import ../../../lib/make-multi-disk-zfs-image.nix {
109 inherit
110 lib
111 config
112 configFile
113 pkgs
114 ;
115 inherit (cfg) contents format;
116 name = config.image.baseName;
117
118 includeChannel = true;
119
120 bootSize = 1000; # 1G is the minimum EBS volume
121
122 rootSize = config.virtualisation.diskSize;
123 rootPoolProperties = {
124 ashift = 12;
125 autoexpand = "on";
126 };
127
128 datasets = config.ec2.zfs.datasets;
129
130 postVM = ''
131 extension=''${rootDiskImage##*.}
132 friendlyName=$out/${config.image.baseName}
133 rootDisk="$friendlyName.root.$extension"
134 bootDisk="$friendlyName.boot.$extension"
135 mv "$rootDiskImage" "$rootDisk"
136 mv "$bootDiskImage" "$bootDisk"
137
138 mkdir -p $out/nix-support
139 echo "file ${cfg.format} $bootDisk" >> $out/nix-support/hydra-build-products
140 echo "file ${cfg.format} $rootDisk" >> $out/nix-support/hydra-build-products
141
142 ${pkgs.jq}/bin/jq -n \
143 --arg system_version ${lib.escapeShellArg config.system.nixos.version} \
144 --arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
145 --arg root_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$rootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
146 --arg boot_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$bootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
147 --arg boot_mode "${amiBootMode}" \
148 --arg root "$rootDisk" \
149 --arg boot "$bootDisk" \
150 '{}
151 | .label = $system_version
152 | .boot_mode = $boot_mode
153 | .system = $system
154 | .disks.boot.logical_bytes = $boot_logical_bytes
155 | .disks.boot.file = $boot
156 | .disks.root.logical_bytes = $root_logical_bytes
157 | .disks.root.file = $root
158 ' > $out/nix-support/image-info.json
159 '';
160 };
161
162 extBuilder = import ../../../lib/make-disk-image.nix {
163 inherit
164 lib
165 config
166 configFile
167 pkgs
168 ;
169
170 inherit (cfg) contents format;
171 inherit (config.image) baseName;
172 name = config.image.baseName;
173
174 fsType = "ext4";
175 partitionTableType = if config.ec2.efi then "efi" else "legacy+gpt";
176
177 inherit (config.virtualisation) diskSize;
178
179 postVM = ''
180 mkdir -p $out/nix-support
181 echo "file ${cfg.format} $diskImage" >> $out/nix-support/hydra-build-products
182
183 ${pkgs.jq}/bin/jq -n \
184 --arg system_version ${lib.escapeShellArg config.system.nixos.version} \
185 --arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
186 --arg logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$diskImage" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
187 --arg boot_mode "${amiBootMode}" \
188 --arg file "$diskImage" \
189 '{}
190 | .label = $system_version
191 | .boot_mode = $boot_mode
192 | .system = $system
193 | .logical_bytes = $logical_bytes
194 | .file = $file
195 | .disks.root.logical_bytes = $logical_bytes
196 | .disks.root.file = $file
197 ' > $out/nix-support/image-info.json
198 '';
199 };
200 in
201 if config.ec2.zfs.enable then zfsBuilder else extBuilder;
202
203 meta.maintainers = with lib.maintainers; [ arianvp ];
204}