confctl 2.2.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/.git-hooks/pre_commit/nixfmt.rb +13 -0
  3. data/.github/workflows/rspec.yml +64 -0
  4. data/.github/workflows/rubocop.yml +27 -0
  5. data/.github/workflows/tests.yml +139 -0
  6. data/.gitignore +12 -7
  7. data/.overcommit.yml +2 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +8 -1
  10. data/AGENTS.md +42 -0
  11. data/CHANGELOG.md +57 -0
  12. data/Gemfile +3 -2
  13. data/Gemfile.lock +198 -0
  14. data/README.md +166 -58
  15. data/Rakefile +5 -0
  16. data/confctl.gemspec +20 -2
  17. data/docs/carrier.md +3 -3
  18. data/docs/flake-inputs.md +159 -0
  19. data/docs/swpins-to-flakes.md +325 -0
  20. data/example/cluster/module-list.nix +2 -1
  21. data/example/cluster/nixos-machine/config.nix +12 -2
  22. data/example/cluster/nixos-machine/hardware.nix +6 -1
  23. data/example/cluster/vpsadminos-container/config.nix +6 -1
  24. data/example/cluster/vpsadminos-container/module.nix +4 -1
  25. data/example/cluster/vpsadminos-machine/config.nix +6 -1
  26. data/example/cluster/vpsadminos-machine/hardware.nix +6 -1
  27. data/example/cluster/vpsadminos-machine/module.nix +5 -2
  28. data/example/cluster/vpsfreecz-vps/config.nix +6 -1
  29. data/example/cluster/vpsfreecz-vps/module.nix +4 -1
  30. data/example/configs/swpins.nix +8 -3
  31. data/example/environments/base.nix +6 -1
  32. data/example/swpins/core.json +35 -0
  33. data/example-flake/.gitignore +2 -0
  34. data/example-flake/README.md +38 -0
  35. data/example-flake/cluster/cluster.nix +5 -0
  36. data/example-flake/cluster/module-list.nix +4 -0
  37. data/example-flake/cluster/nested/nixos-machine/config.nix +25 -0
  38. data/example-flake/cluster/nested/nixos-machine/hardware.nix +9 -0
  39. data/example-flake/cluster/nested/nixos-machine/module.nix +8 -0
  40. data/example-flake/cluster/nixos-machine/config.nix +25 -0
  41. data/example-flake/cluster/nixos-machine/hardware.nix +9 -0
  42. data/example-flake/cluster/nixos-machine/module.nix +8 -0
  43. data/example-flake/cluster/vpsadminos-container/config.nix +28 -0
  44. data/example-flake/cluster/vpsadminos-container/module.nix +8 -0
  45. data/example-flake/cluster/vpsadminos-machine/config.nix +27 -0
  46. data/example-flake/cluster/vpsadminos-machine/hardware.nix +9 -0
  47. data/example-flake/cluster/vpsadminos-machine/module.nix +8 -0
  48. data/example-flake/cluster/vpsfreecz-vps/config.nix +31 -0
  49. data/example-flake/cluster/vpsfreecz-vps/module.nix +8 -0
  50. data/example-flake/configs/confctl.nix +10 -0
  51. data/example-flake/data/default.nix +5 -0
  52. data/example-flake/data/ssh-keys.nix +7 -0
  53. data/example-flake/environments/base.nix +18 -0
  54. data/example-flake/flake.lock +75 -0
  55. data/example-flake/flake.nix +36 -0
  56. data/example-flake/modules/module-list.nix +13 -0
  57. data/example-flake/shell.nix +11 -0
  58. data/flake.lock +159 -0
  59. data/flake.nix +189 -0
  60. data/gemset.nix +1022 -0
  61. data/lib/confctl/cli/app.rb +145 -0
  62. data/lib/confctl/cli/attr_filters.rb +1 -1
  63. data/lib/confctl/cli/cluster.rb +681 -108
  64. data/lib/confctl/cli/command.rb +24 -2
  65. data/lib/confctl/cli/configuration.rb +171 -106
  66. data/lib/confctl/cli/generation.rb +65 -1
  67. data/lib/confctl/cli/inputs/channels.rb +190 -0
  68. data/lib/confctl/cli/inputs/machines.rb +83 -0
  69. data/lib/confctl/cli/inputs/root.rb +101 -0
  70. data/lib/confctl/cli/inputs.rb +5 -0
  71. data/lib/confctl/cli/log_view.rb +21 -10
  72. data/lib/confctl/cli/migrate/swpins_to_flakes.rb +866 -0
  73. data/lib/confctl/cli/migrate.rb +5 -0
  74. data/lib/confctl/cli/output_formatter.rb +5 -7
  75. data/lib/confctl/cli/swpins/base.rb +9 -0
  76. data/lib/confctl/cli/swpins/channel.rb +2 -5
  77. data/lib/confctl/cli/swpins/cluster.rb +2 -5
  78. data/lib/confctl/cli/swpins/core.rb +2 -5
  79. data/lib/confctl/config_type.rb +7 -0
  80. data/lib/confctl/flake_lock.rb +78 -0
  81. data/lib/confctl/flake_lock_diff.rb +36 -0
  82. data/lib/confctl/generation/build.rb +131 -24
  83. data/lib/confctl/generation/build_list.rb +4 -3
  84. data/lib/confctl/generation/unified.rb +14 -1
  85. data/lib/confctl/git_repo_mirror.rb +2 -2
  86. data/lib/confctl/health_checks/run_command.rb +3 -2
  87. data/lib/confctl/health_checks/systemd/properties.rb +1 -1
  88. data/lib/confctl/health_checks/systemd/property_list.rb +2 -2
  89. data/lib/confctl/inputs/commit_message.rb +125 -0
  90. data/lib/confctl/inputs/git_commit.rb +17 -0
  91. data/lib/confctl/inputs/nix_output_guard.rb +37 -0
  92. data/lib/confctl/inputs/setter.rb +179 -0
  93. data/lib/confctl/inputs/updater.rb +76 -0
  94. data/lib/confctl/inputs.rb +5 -0
  95. data/lib/confctl/inputs_info.rb +50 -0
  96. data/lib/confctl/line_buffer.rb +1 -1
  97. data/lib/confctl/machine.rb +18 -3
  98. data/lib/confctl/machine_control.rb +8 -2
  99. data/lib/confctl/machine_list.rb +2 -2
  100. data/lib/confctl/machine_status.rb +63 -20
  101. data/lib/confctl/nix/args.rb +48 -0
  102. data/lib/confctl/nix.rb +30 -437
  103. data/lib/confctl/nix_build_flake.rb +95 -0
  104. data/lib/confctl/nix_copy.rb +14 -2
  105. data/lib/confctl/nix_flake.rb +449 -0
  106. data/lib/confctl/nix_format.rb +3 -3
  107. data/lib/confctl/nix_legacy.rb +467 -0
  108. data/lib/confctl/swpins/change_set.rb +29 -3
  109. data/lib/confctl/swpins/specs/base.rb +2 -2
  110. data/lib/confctl/ui.rb +19 -0
  111. data/lib/confctl/version.rb +1 -1
  112. data/man/index.html +11 -0
  113. data/man/man8/confctl-options.nix.8.html +113 -0
  114. data/man/man8/confctl.8 +152 -21
  115. data/man/man8/confctl.8.html +355 -0
  116. data/man/man8/confctl.8.md +148 -17
  117. data/man/style.css +301 -0
  118. data/nix/evaluator.nix +94 -69
  119. data/nix/flake/mk-confctl-devshell.nix +85 -0
  120. data/nix/flake/mk-confctl-outputs.nix +522 -0
  121. data/nix/flake/mk-config-devshell.nix +168 -0
  122. data/nix/lib/default.nix +118 -65
  123. data/nix/lib/machine/default.nix +65 -45
  124. data/nix/lib/machine/info.nix +16 -5
  125. data/nix/lib/swpins/eval.nix +42 -29
  126. data/nix/lib/swpins/options.nix +6 -2
  127. data/nix/machines.nix +23 -15
  128. data/nix/modules/cluster/default.nix +104 -41
  129. data/nix/modules/confctl/carrier/base.nix +11 -4
  130. data/nix/modules/confctl/carrier/carrier-env.rb +2 -2
  131. data/nix/modules/confctl/carrier/netboot/build-netboot-server.rb +50 -16
  132. data/nix/modules/confctl/carrier/netboot/nixos.nix +56 -24
  133. data/nix/modules/confctl/configuration-info.nix +17 -0
  134. data/nix/modules/confctl/generations.nix +2 -2
  135. data/nix/modules/confctl/host.nix +13 -0
  136. data/nix/modules/confctl/inputs-info.nix +21 -0
  137. data/nix/modules/confctl/kexec-netboot/default.nix +13 -6
  138. data/nix/modules/confctl/kexec-netboot/kexec-netboot.8.adoc +3 -0
  139. data/nix/modules/confctl/kexec-netboot/kexec-netboot.rb +25 -16
  140. data/nix/modules/confctl/nix.nix +31 -1
  141. data/nix/modules/confctl/swpins.nix +13 -6
  142. data/nix/modules/module-list.nix +4 -2
  143. data/nix/modules/system-list.nix +4 -1
  144. data/nix/package.nix +37 -0
  145. data/shell.nix +39 -8
  146. data/skills/confctl-configuration-update/SKILL.md +228 -0
  147. data/skills/confctl-configuration-update/agents/openai.yaml +4 -0
  148. data/skills/confctl-release/SKILL.md +102 -0
  149. data/skills/confctl-release/agents/openai.yaml +4 -0
  150. data/spec/confctl/cli/cluster_health_check_spec.rb +43 -0
  151. data/spec/confctl/cli/cluster_skip_current_deploy_spec.rb +157 -0
  152. data/spec/confctl/cli/cluster_status_flake_spec.rb +83 -0
  153. data/spec/confctl/cli/inputs_set_output_spec.rb +70 -0
  154. data/spec/confctl/configuration_spec.rb +45 -0
  155. data/spec/confctl/inputs_spec.rb +104 -0
  156. data/spec/confctl/machine_control_spec.rb +112 -0
  157. data/spec/confctl/machine_list_spec.rb +47 -0
  158. data/spec/confctl/nix_flake_spec.rb +41 -0
  159. data/spec/confctl/swpins_spec.rb +106 -0
  160. data/spec/generation/build_modes_spec.rb +132 -0
  161. data/spec/inputs/commit_message_spec.rb +205 -0
  162. data/spec/inputs/nix_cached_fallback_spec.rb +50 -0
  163. data/spec/inputs/nix_output_guard_spec.rb +45 -0
  164. data/spec/spec_helper.rb +12 -0
  165. data/spec/support/cli_helper.rb +57 -0
  166. data/test-runner.sh +7 -0
  167. data/tests/all-tests.nix +30 -0
  168. data/tests/make-test.nix +15 -0
  169. data/tests/runner/extensions/confctl_helpers.rb +238 -0
  170. data/tests/runner/extensions/hostfwd_ports.rb +41 -0
  171. data/tests/suite/auto_rollback.nix +344 -0
  172. data/tests/suite/carrier/deploy.nix +751 -0
  173. data/tests/suite/carrier/netboot.nix +849 -0
  174. data/tests/suite/deploy/base.nix +806 -0
  175. data/tests/suite/deploy/flakes.nix +1 -0
  176. data/tests/suite/deploy/swpins.nix +1 -0
  177. metadata +104 -4
  178. data/nix/modules/confctl/overlays.nix +0 -15
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'osvm'
4
+
5
+ module ConfctlHostfwdPorts
6
+ PLACEHOLDER = /\bnet\d+\b/
7
+ @ports = {}
8
+
9
+ class << self
10
+ def reserve(name)
11
+ key = name.to_s
12
+ @ports[key] ||= OsVm::PortReservation.get_port(key: "hostfwd:#{key}")
13
+ end
14
+
15
+ def port(name)
16
+ @ports[name.to_s]
17
+ end
18
+
19
+ def ports
20
+ @ports.dup
21
+ end
22
+
23
+ def replace_placeholders(str)
24
+ str.gsub(PLACEHOLDER) { |token| reserve(token).to_s }
25
+ end
26
+ end
27
+ end
28
+
29
+ module ConfctlHostfwdUserNetworkPatch
30
+ def qemu_options
31
+ host_forward = @opts['hostForward']
32
+ return super unless host_forward.is_a?(String)
33
+
34
+ @opts['hostForward'] = ConfctlHostfwdPorts.replace_placeholders(host_forward)
35
+ super
36
+ ensure
37
+ @opts['hostForward'] = host_forward
38
+ end
39
+ end
40
+
41
+ OsVm::MachineConfig::UserNetwork.prepend(ConfctlHostfwdUserNetworkPatch)
@@ -0,0 +1,344 @@
1
+ import ../make-test.nix (
2
+ {
3
+ pkgs,
4
+ confctlPackage,
5
+ confctlSrc,
6
+ ...
7
+ }:
8
+ let
9
+ confctlBin =
10
+ if confctlPackage == null then
11
+ throw "suiteArgs.confctlPackage is required"
12
+ else
13
+ "${confctlPackage}/bin/confctl";
14
+
15
+ confctlSource = if confctlSrc == null then throw "suiteArgs.confctlSrc is required" else confctlSrc;
16
+ in
17
+ {
18
+ name = "auto_rollback";
19
+
20
+ description = ''
21
+ Verify deterministic auto-rollback for multiple failure modes.
22
+ '';
23
+
24
+ tags = [
25
+ "ci"
26
+ ];
27
+
28
+ machines = {
29
+ nixos = {
30
+ spin = "nixos";
31
+ diskSize = 20480;
32
+ networks = [
33
+ {
34
+ type = "user";
35
+ opts = {
36
+ hostForward = "tcp::net1-:22";
37
+ network = "10.0.2.0/24";
38
+ host = "10.0.2.2";
39
+ dns = "10.0.2.3";
40
+ };
41
+ }
42
+ ];
43
+ config = {
44
+ services.openssh = {
45
+ enable = true;
46
+ settings = {
47
+ PermitRootLogin = "yes";
48
+ PasswordAuthentication = false;
49
+ };
50
+ };
51
+ networking.firewall.enable = false;
52
+ environment.systemPackages = with pkgs; [
53
+ git
54
+ ];
55
+ };
56
+ };
57
+ };
58
+
59
+ testScript = ''
60
+ require 'fileutils'
61
+
62
+ NIXOS_MACHINE = 'nixos-machine'
63
+
64
+ FAILURE_NETWORK_CONFIG = {
65
+ 'flush_addresses' => <<~NIX,
66
+ networking.useDHCP = false;
67
+ networking.dhcpcd.enable = false;
68
+ networking.interfaces.eth0.useDHCP = false;
69
+ networking.interfaces.eth0.ipv4.addresses = [ ];
70
+ networking.interfaces.eth0.ipv6.addresses = [ ];
71
+ NIX
72
+ 'flush_routes' => <<~NIX,
73
+ networking.useDHCP = false;
74
+ networking.dhcpcd.enable = false;
75
+ networking.interfaces.eth0.useDHCP = false;
76
+ networking.interfaces.eth0.ipv4.addresses = [
77
+ {
78
+ address = "10.0.2.15";
79
+ prefixLength = 32;
80
+ }
81
+ ];
82
+ networking.interfaces.eth0.ipv6.addresses = [ ];
83
+ NIX
84
+ 'link_down' => <<~NIX
85
+ systemd.services.confctl-break-link-down = {
86
+ description = "Break network by forcing eth0 down";
87
+ wantedBy = [ "multi-user.target" ];
88
+ after = [ "network-online.target" "sshd.service" ];
89
+ serviceConfig = {
90
+ Type = "oneshot";
91
+ RemainAfterExit = true;
92
+ ExecStart = "${pkgs.iproute2}/bin/ip link set dev eth0 down";
93
+ ExecStop = "${pkgs.iproute2}/bin/ip link set dev eth0 up";
94
+ };
95
+ };
96
+ NIX
97
+ }
98
+
99
+ def network_breaker_nix(mode)
100
+ return "" if mode.nil?
101
+
102
+ FAILURE_NETWORK_CONFIG.fetch(mode)
103
+ end
104
+
105
+ def write_nixos_config!(conf_dir, marker:, failure_mode: nil)
106
+ breaker = network_breaker_nix(failure_mode)
107
+
108
+ File.write(File.join(conf_dir, 'cluster/nixos-machine/config.nix'), <<~NIX)
109
+ {
110
+ config,
111
+ pkgs,
112
+ lib,
113
+ inputs,
114
+ ...
115
+ }:
116
+ {
117
+ imports = [
118
+ ../../environments/base.nix
119
+ ./hardware.nix
120
+ (
121
+ { lib, ... }:
122
+ {
123
+ options.virtualisation.cores = lib.mkOption {
124
+ type = lib.types.int;
125
+ default = 2;
126
+ };
127
+ options.virtualisation.memorySize = lib.mkOption {
128
+ type = lib.types.int;
129
+ default = 2048;
130
+ };
131
+ options.virtualisation.mountHostNixStore = lib.mkOption {
132
+ type = lib.types.bool;
133
+ default = false;
134
+ };
135
+ options.virtualisation.sharedDirectories = lib.mkOption {
136
+ type = lib.types.attrsOf lib.types.anything;
137
+ default = { };
138
+ };
139
+ }
140
+ )
141
+ (inputs.vpsadminos + "/tests/configs/nixos/base.nix")
142
+ ];
143
+
144
+ networking.hostName = "nixos-machine";
145
+ time.timeZone = lib.mkForce "UTC";
146
+ boot.loader.grub.enable = false;
147
+
148
+ fileSystems."/" = {
149
+ device = "/dev/disk/by-label/nixos";
150
+ fsType = "ext4";
151
+ };
152
+
153
+ environment.etc."confctl-marker".text = "#{marker}\n";
154
+ #{breaker}
155
+ }
156
+ NIX
157
+ end
158
+
159
+ def prepare_fixture!(conf_dir:, port:, pubkey:)
160
+ prepare_fixture_dir!(conf_dir)
161
+
162
+ File.write(File.join(conf_dir, 'flake.nix'), <<~NIX)
163
+ {
164
+ description = "confctl auto-rollback test fixture";
165
+
166
+ inputs = {
167
+ confctl.url = "path:#{confctl_src}";
168
+ nixpkgs.follows = "confctl/nixpkgs";
169
+ vpsadminos.follows = "confctl/vpsadminos";
170
+ };
171
+
172
+ outputs = inputs@{ self, confctl, ... }:
173
+ let
174
+ channels = {
175
+ nixos = {
176
+ nixpkgs = "nixpkgs";
177
+ vpsadminos = "vpsadminos";
178
+ };
179
+ };
180
+
181
+ confctlOutputs = confctl.lib.mkConfctlOutputs {
182
+ confDir = ./.;
183
+ inherit inputs channels;
184
+ };
185
+ in
186
+ {
187
+ confctl = confctlOutputs;
188
+
189
+ devShells.x86_64-linux.default = confctl.lib.mkConfigDevShell {
190
+ system = "x86_64-linux";
191
+ mode = "minimal";
192
+ };
193
+ };
194
+ }
195
+ NIX
196
+
197
+ File.write(File.join(conf_dir, 'cluster/nixos-machine/module.nix'), <<~NIX)
198
+ { config, ... }:
199
+ {
200
+ cluster."nixos-machine" = {
201
+ spin = "nixos";
202
+ inputs.channels = [ "nixos" ];
203
+ host.target = "127.0.0.1";
204
+ host.port = #{port};
205
+ autoRollback.timeout = 15;
206
+ };
207
+ }
208
+ NIX
209
+
210
+ write_nixos_config!(conf_dir, marker: 'baseline', failure_mode: nil)
211
+
212
+ write_cluster_modules!(conf_dir, [ 'nixos-machine/module.nix' ])
213
+ write_admin_ssh_keys!(conf_dir, pubkey)
214
+ init_fixture_repo!(conf_dir)
215
+ end
216
+
217
+ def machine_state(machine_name)
218
+ confctl_machine_state(machine_name)
219
+ end
220
+
221
+ def assert_machine_state(machine_name, profile:, current:)
222
+ expect(confctl_remote_realpath(machine_name, '/nix/var/nix/profiles/system')).to eq(profile)
223
+ expect(confctl_remote_realpath(machine_name, '/run/current-system')).to eq(current)
224
+ end
225
+
226
+ def assert_store_path_exists(machine_name, store_path)
227
+ confctl_store_path_exists!(machine_name, store_path)
228
+ end
229
+
230
+ def deploy_with_retry!(*deploy_args, attempts: 3)
231
+ last_error = nil
232
+
233
+ attempts.times do |i|
234
+ begin
235
+ return confctl!('deploy', '--yes', *deploy_args)
236
+ rescue StandardError => e
237
+ last_error = e
238
+ raise if i + 1 >= attempts
239
+
240
+ sleep(2)
241
+ wait_for_confctl_connectivity!(expected_successes: 1, timeout: 120)
242
+ end
243
+ end
244
+
245
+ raise(last_error || 'deploy_with_retry! failed')
246
+ end
247
+
248
+ def exercise_failure_mode!(mode, idx, baseline_state:, baseline_gen:)
249
+ deploy_with_retry!('--generation', baseline_gen['name'], NIXOS_MACHINE, 'switch')
250
+ assert_machine_state(
251
+ NIXOS_MACHINE,
252
+ profile: baseline_state[:profile],
253
+ current: baseline_state[:current]
254
+ )
255
+
256
+ write_nixos_config!(@conf_dir, marker: "#{mode}-#{idx}", failure_mode: mode)
257
+ confctl!('build', '--yes', NIXOS_MACHINE)
258
+ broken_gen = confctl_generation_info(NIXOS_MACHINE)
259
+
260
+ expect(broken_gen['toplevel']).not_to eq(baseline_gen['toplevel'])
261
+
262
+ _out, _err, status = confctl('deploy', '--yes', '--generation', 'current', NIXOS_MACHINE)
263
+ expect(status.success?).to be(false)
264
+
265
+ wait_for_block(name: "rollback after #{mode}", timeout: 240) do
266
+ state = machine_state(NIXOS_MACHINE)
267
+ state[:current] == baseline_state[:current] && state[:profile] == baseline_state[:profile]
268
+ end
269
+
270
+ assert_machine_state(
271
+ NIXOS_MACHINE,
272
+ profile: baseline_state[:profile],
273
+ current: baseline_state[:current]
274
+ )
275
+ assert_store_path_exists(NIXOS_MACHINE, broken_gen['toplevel'])
276
+
277
+ out = wait_for_confctl_connectivity!(expected_successes: 1, timeout: 180)
278
+ expect(out).to include('1 successful')
279
+
280
+ confctl_ssh!(NIXOS_MACHINE, 'sh', '-c', 'umount /run/confctl || true')
281
+ end
282
+
283
+ before(:suite) do
284
+ @port = ConfctlHostfwdPorts.reserve('net1')
285
+ nixos.start
286
+
287
+ @state_dir = @opts[:state_dir]
288
+ @conf_dir = File.join(@state_dir, 'conf')
289
+ @home_dir = File.join(@state_dir, 'home')
290
+ confctl_setup!(
291
+ bin: "${confctlBin}",
292
+ src: "${confctlSource}",
293
+ conf_dir: @conf_dir,
294
+ home_dir: @home_dir
295
+ )
296
+
297
+ @pubkey = setup_ssh_home!(@home_dir)
298
+ install_pubkey!(nixos, @pubkey)
299
+ prepare_fixture!(conf_dir: @conf_dir, port: @port, pubkey: @pubkey)
300
+ end
301
+
302
+ describe 'auto rollback', order: :defined do
303
+ before(:context) do
304
+ out = wait_for_confctl_connectivity!(expected_successes: 1, timeout: 180)
305
+ expect(out).to include('1 successful')
306
+
307
+ deploy_with_retry!(NIXOS_MACHINE)
308
+ @baseline_gen = confctl_generation_info(NIXOS_MACHINE)
309
+ @baseline_state = machine_state(NIXOS_MACHINE)
310
+
311
+ expect(@baseline_gen['toplevel']).to eq(@baseline_state[:current])
312
+ expect(@baseline_gen['toplevel']).to eq(@baseline_state[:profile])
313
+ end
314
+
315
+ it 'rolls back when IPv4 and IPv6 addresses are removed' do
316
+ exercise_failure_mode!(
317
+ 'flush_addresses',
318
+ 0,
319
+ baseline_state: @baseline_state,
320
+ baseline_gen: @baseline_gen
321
+ )
322
+ end
323
+
324
+ it 'rolls back when routes are removed' do
325
+ exercise_failure_mode!(
326
+ 'flush_routes',
327
+ 1,
328
+ baseline_state: @baseline_state,
329
+ baseline_gen: @baseline_gen
330
+ )
331
+ end
332
+
333
+ it 'rolls back when interface link is brought down' do
334
+ exercise_failure_mode!(
335
+ 'link_down',
336
+ 2,
337
+ baseline_state: @baseline_state,
338
+ baseline_gen: @baseline_gen
339
+ )
340
+ end
341
+ end
342
+ '';
343
+ }
344
+ )