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
@@ -1,5 +1,6 @@
1
1
  require 'json'
2
2
  require 'tty-command'
3
+ require 'confctl/inputs_info'
3
4
 
4
5
  module ConfCtl
5
6
  class MachineStatus
@@ -73,9 +74,15 @@ module ConfCtl
73
74
  # @return [Hash]
74
75
  attr_accessor :target_swpin_specs
75
76
 
77
+ # @return [Hash]
78
+ attr_accessor :target_inputs_info
79
+
76
80
  # @return [Hash]
77
81
  attr_reader :swpins_info
78
82
 
83
+ # @return [Hash]
84
+ attr_reader :inputs_info
85
+
79
86
  # @return [Hash]
80
87
  attr_reader :swpins_state
81
88
 
@@ -86,7 +93,7 @@ module ConfCtl
86
93
  end
87
94
 
88
95
  # Connect to the machine and query its state
89
- def query(toplevel: true, generations: true)
96
+ def query(toplevel: true, generations: true, swpins: true, inputs: false)
90
97
  begin
91
98
  @uptime = mc.uptime
92
99
  rescue TTY::Command::ExitError
@@ -109,8 +116,18 @@ module ConfCtl
109
116
  end
110
117
  end
111
118
 
119
+ if swpins
120
+ begin
121
+ @swpins_info = query_swpins
122
+ rescue Error
123
+ nil
124
+ end
125
+ end
126
+
127
+ return unless inputs
128
+
112
129
  begin
113
- @swpins_info = query_swpins
130
+ @inputs_info = query_inputs_info
114
131
  rescue Error
115
132
  nil
116
133
  end
@@ -145,12 +162,7 @@ module ConfCtl
145
162
  end
146
163
 
147
164
  def query_swpins
148
- json =
149
- if machine.carried?
150
- query_carried_swpins
151
- else
152
- mc.read_file('/etc/confctl/swpins-info.json')
153
- end
165
+ json = read_swpins_info_json
154
166
 
155
167
  case json
156
168
  when String
@@ -160,22 +172,53 @@ module ConfCtl
160
172
  end
161
173
  end
162
174
 
163
- # @return [Hash, String]
164
- def query_carried_swpins
165
- begin
166
- json = mc.read_file(File.join(machine.profile, 'machine.json'))
167
- parsed = JSON.parse(json)
168
- return parsed['swpins-info'] if parsed['swpins-info']
169
- rescue TTY::Command::ExitError
170
- # pass
175
+ def query_inputs_info
176
+ json = read_inputs_info_json
177
+
178
+ case json
179
+ when String
180
+ InputsInfo.parse(json)
181
+ when Hash
182
+ InputsInfo.normalize(json)
171
183
  end
184
+ end
172
185
 
173
- begin
174
- return mc.read_file(File.join(machine.profile, '/etc/confctl/swpins-info.json'))
175
- rescue TTY::Command::ExitError
176
- # pass
186
+ # @return [Hash, String, nil]
187
+ def read_swpins_info_json
188
+ if machine.carried?
189
+ read_carried_info_json('swpins-info', '/etc/confctl/swpins-info.json')
190
+ else
191
+ read_file('/etc/confctl/swpins-info.json')
192
+ end
193
+ end
194
+
195
+ # @return [Hash, String, nil]
196
+ def read_inputs_info_json
197
+ if machine.carried?
198
+ read_carried_info_json('inputs-info', '/etc/confctl/inputs-info.json')
199
+ else
200
+ read_file('/etc/confctl/inputs-info.json')
177
201
  end
202
+ end
203
+
204
+ def read_carried_info_json(key, path)
205
+ info = read_carried_machine_json(key)
206
+ return info if info
207
+
208
+ read_file(File.join(machine.profile, path))
209
+ end
210
+
211
+ def read_carried_machine_json(key)
212
+ json = mc.read_file(File.join(machine.profile, 'machine.json'))
213
+ parsed = JSON.parse(json)
214
+ parsed[key] if parsed[key]
215
+ rescue TTY::Command::ExitError, JSON::ParserError
216
+ nil
217
+ end
178
218
 
219
+ def read_file(path)
220
+ mc.read_file(path)
221
+ rescue TTY::Command::ExitError
179
222
  nil
180
223
  end
181
224
  end
@@ -0,0 +1,48 @@
1
+ module ConfCtl
2
+ class Nix
3
+ class Args
4
+ DEFAULT_LEGACY_NAMES = %w[nixpkgs vpsadminos vpsadmin].freeze
5
+
6
+ def initialize(settings:, impure: nil, no_update_lock_file: true)
7
+ @settings = settings || {}
8
+ @impure_override = impure
9
+ @no_update_lock_file = no_update_lock_file
10
+ end
11
+
12
+ def impure?
13
+ return @impure_override unless @impure_override.nil?
14
+
15
+ @settings.dig('nix', 'impureEval') == true
16
+ end
17
+
18
+ def legacy_nix_path?
19
+ @settings.dig('nix', 'legacyNixPath') == true
20
+ end
21
+
22
+ def legacy_names
23
+ names = @settings.dig('nix', 'legacyNixPathMap')
24
+ return DEFAULT_LEGACY_NAMES if names.nil?
25
+
26
+ names
27
+ end
28
+
29
+ def eval_args
30
+ base_args
31
+ end
32
+
33
+ def build_args
34
+ base_args
35
+ end
36
+
37
+ private
38
+
39
+ def base_args
40
+ args = []
41
+ args << '--impure' if impure?
42
+ args << '--no-write-lock-file'
43
+ args << '--no-update-lock-file' if @no_update_lock_file
44
+ args
45
+ end
46
+ end
47
+ end
48
+ end
data/lib/confctl/nix.rb CHANGED
@@ -1,450 +1,43 @@
1
- require 'confctl/utils/file'
2
- require 'digest'
3
- require 'etc'
4
- require 'json'
5
- require 'securerandom'
6
- require 'tempfile'
1
+ require 'forwardable'
2
+ require_relative 'config_type'
3
+ require_relative 'nix_legacy'
4
+ require_relative 'nix_flake'
7
5
 
8
6
  module ConfCtl
9
7
  class Nix
8
+ extend Forwardable
9
+
10
10
  # Create a new instance without access to {ConfCtl::Settings}, i.e. when
11
11
  # called outside of cluster configuration directory.
12
12
  # @return [Nix]
13
13
  def self.stateless(show_trace: false, max_jobs: 'auto')
14
- new(show_trace:, max_jobs:)
14
+ new(show_trace: show_trace, max_jobs: max_jobs)
15
15
  end
16
16
 
17
- include Utils::File
18
-
19
17
  def initialize(conf_dir: nil, show_trace: false, max_jobs: nil, cores: nil)
20
18
  @conf_dir = conf_dir || ConfDir.path
21
- @show_trace = show_trace
22
- @max_jobs = max_jobs || Settings.instance.max_jobs
23
- @cores = cores
24
- @cmd = SystemCommand.new
25
- end
26
-
27
- def confctl_settings
28
- out_link = File.join(
29
- cache_dir,
30
- 'build',
31
- 'settings.json'
32
- )
33
-
34
- with_cache(out_link) do
35
- with_argument({
36
- confDir: conf_dir,
37
- build: :confctl
38
- }) do |arg|
39
- cmd_args = [
40
- 'nix-build',
41
- '--arg', 'jsonArg', arg,
42
- '--out-link', out_link,
43
- (show_trace ? '--show-trace' : nil),
44
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
45
- (cores ? ['--cores', cores.to_s] : nil),
46
- ConfCtl.nix_asset('evaluator.nix')
47
- ].flatten.compact
48
-
49
- cmd.run(*cmd_args)
50
- end
51
- end
52
-
53
- demodulify(JSON.parse(File.read(out_link))['confctl'])
54
- end
55
-
56
- # Returns an array with options from all confctl modules
57
- # @return [Array]
58
- def module_options
59
- options = nix_instantiate({
60
- confDir: conf_dir,
61
- build: :moduleOptions
62
- })
63
- end
64
-
65
- # Returns an array with machine fqdns
66
- # @return [Array<String>]
67
- def list_machine_fqdns
68
- nix_instantiate({
69
- confDir: conf_dir,
70
- build: :list
71
- })['machines']
72
- end
73
-
74
- # Return machines and their config in a hash
75
- # @return [Hash]
76
- def list_machines
77
- out_link = File.join(
78
- cache_dir,
79
- 'build',
80
- 'machine-list.json'
81
- )
82
-
83
- with_cache(out_link) do
84
- with_argument({
85
- confDir: conf_dir,
86
- build: :info
87
- }, core_swpins: true) do |arg|
88
- cmd_args = [
89
- 'nix-build',
90
- '--arg', 'jsonArg', arg,
91
- '--out-link', out_link,
92
- (show_trace ? '--show-trace' : nil),
93
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
94
- (cores ? ['--cores', cores.to_s] : nil),
95
- ConfCtl.nix_asset('evaluator.nix')
96
- ].flatten.compact
97
-
98
- cmd.run(*cmd_args)
99
- end
100
- end
101
-
102
- demodulify(JSON.parse(File.read(out_link)))
103
- end
104
-
105
- def list_swpins_channels
106
- nix_instantiate({
107
- confDir: conf_dir,
108
- build: :listSwpinsChannels
109
- })
110
- end
111
-
112
- # Evaluate swpins for host
113
- # @return [Hash]
114
- def eval_core_swpins
115
- with_argument({
116
- confDir: conf_dir,
117
- build: :evalCoreSwpins
118
- }) do |arg|
119
- out_link = File.join(
120
- cache_dir,
121
- 'build',
122
- 'core.swpins'
123
- )
124
-
125
- cmd_args = [
126
- 'nix-build',
127
- '--arg', 'jsonArg', arg,
128
- '--out-link', out_link,
129
- (show_trace ? '--show-trace' : nil),
130
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
131
- (cores ? ['--cores', cores.to_s] : nil),
132
- ConfCtl.nix_asset('evaluator.nix')
133
- ].flatten.compact
134
-
135
- out, = cmd.run(*cmd_args).stdout
136
-
137
- JSON.parse(File.read(out.strip))
138
- end
139
- end
140
-
141
- # Evaluate swpins for hosts
142
- # @param hosts [Array<String>]
143
- # @return [Hash] host => swpins
144
- def eval_host_swpins(hosts)
145
- with_argument({
146
- confDir: conf_dir,
147
- build: :evalHostSwpins,
148
- machines: hosts
149
- }, core_swpins: true) do |arg|
150
- out_link = File.join(
151
- cache_dir,
152
- 'build',
153
- "#{Digest::SHA256.hexdigest(hosts.join(','))[0..11]}.swpins"
154
- )
155
-
156
- cmd_args = [
157
- 'nix-build',
158
- '--arg', 'jsonArg', arg,
159
- '--out-link', out_link,
160
- (show_trace ? '--show-trace' : nil),
161
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
162
- (cores ? ['--cores', cores.to_s] : nil),
163
- ConfCtl.nix_asset('evaluator.nix')
164
- ].flatten.compact
165
-
166
- out, = cmd.run(*cmd_args)
167
-
168
- JSON.parse(File.read(out.strip))
169
- end
170
- end
171
-
172
- # Build config.system.build.toplevel for selected hosts
173
- #
174
- # @param hosts [Array<Machine>]
175
- # @param swpin_paths [Hash]
176
- # @param host_swpin_specs [Hash]
177
- # @param time [Time]
178
- #
179
- # @yieldparam type [:build, :fetch]
180
- # @yieldparam progress [Integer]
181
- # @yieldparam total [Integer]
182
- # @yieldparam path [String]
183
- #
184
- # @return [Hash<String, Generation::Build>]
185
- def build_attributes(hosts: [], swpin_paths: {}, host_swpin_specs: {}, time: nil, &block)
186
- with_argument({
187
- confDir: conf_dir,
188
- build: :toplevel,
189
- machines: hosts
190
- }) do |arg|
191
- time ||= Time.now
192
- ret_generations = {}
193
- out_link = File.join(cache_dir, 'build', "#{SecureRandom.hex(4)}.build")
194
-
195
- cmd_args = [
196
- '--arg', 'jsonArg', arg,
197
- '--out-link', out_link,
198
- (show_trace ? '--show-trace' : nil),
199
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
200
- (cores ? ['--cores', cores.to_s] : nil),
201
- ConfCtl.nix_asset('evaluator.nix')
202
- ].flatten.compact
203
-
204
- nb = NixBuild.new(cmd_args, swpin_paths)
205
- nb.run(&block)
206
-
207
- begin
208
- host_results = JSON.parse(File.read(out_link))
209
- host_results.each do |host, result|
210
- host_generations = Generation::BuildList.new(host)
211
- generation = host_generations.find(result['attribute'], swpin_paths)
212
-
213
- if generation.nil?
214
- generation = Generation::Build.new(host)
215
- generation.create(
216
- result['attribute'],
217
- result['autoRollback'],
218
- swpin_paths,
219
- host_swpin_specs[host],
220
- date: time
221
- )
222
- generation.save
223
- end
224
-
225
- host_generations.current = generation
226
- ret_generations[host] = generation
227
- end
228
- ensure
229
- unlink_if_exists(out_link)
230
- end
231
-
232
- ret_generations
233
- end
234
- end
235
-
236
- # @param machine [Machine]
237
- # @param paths [Array<String>]
238
- #
239
- # @yieldparam progress [Integer]
240
- # @yieldparam total [Integer]
241
- # @yieldparam path [String]
242
- #
243
- # @return [Boolean]
244
- def copy(machine, paths, &)
245
- if machine.localhost?
246
- true
247
- elsif machine.carried?
248
- cp = NixCopy.new(machine.carrier_machine.target_host, paths)
249
- cp.run!(&).success?
250
- else
251
- cp = NixCopy.new(machine.target_host, paths)
252
- cp.run!(&).success?
253
- end
254
- end
255
-
256
- # @param machine [Machine]
257
- # @param generation [Generation::Build]
258
- # @param action [String]
259
- # @return [Boolean]
260
- def activate(machine, generation, action)
261
- args = [File.join(generation.toplevel, 'bin/switch-to-configuration'), action]
262
-
263
- MachineControl.new(machine).execute!(*args).success?
264
- end
265
-
266
- # @param machine [Machine]
267
- # @param generation [Generation::Build]
268
- # @param action [String]
269
- # @return [Boolean]
270
- def activate_with_rollback(machine, generation, action)
271
- check_file = File.join('/run', "confctl-confirm-#{SecureRandom.hex(3)}")
272
- timeout = machine['autoRollback']['timeout']
273
- logger = NullLogger.new
274
-
275
- args = [
276
- generation.auto_rollback,
277
- '-t', timeout,
278
- generation.toplevel,
279
- action,
280
- check_file
281
- ]
282
-
283
- activation_success = nil
284
-
285
- activation_thread = Thread.new do
286
- activation_success = MachineControl.new(machine).execute!(*args).success?
287
- end
288
-
289
- # Wait for the configuration to be switched
290
- t = Time.now
291
-
292
- loop do
293
- out, = MachineControl.new(machine, logger:).execute!('cat', check_file, '2>/dev/null')
294
- stripped = out.strip
295
- break if stripped == 'switched' || ((t + timeout + 10) < Time.now && stripped != 'switching')
296
-
297
- sleep(1)
298
- end
299
-
300
- # Confirm it
301
- 10.times do
302
- break if MachineControl.new(machine, logger:).execute!('sh', '-c', "'echo confirmed > #{check_file}'").success?
303
- end
304
-
305
- activation_thread.join
306
- activation_success
307
- end
308
-
309
- # @param machine [Machine]
310
- # @param toplevel [String]
311
- # @return [Boolean]
312
- def set_profile(machine, toplevel)
313
- args = [
314
- 'nix-env',
315
- '-p', machine.profile,
316
- '--set', toplevel
317
- ]
318
-
319
- MachineControl.new(machine).execute!(*args).success?
320
- end
321
-
322
- # @param machine [Machine]
323
- # @param toplevel [String]
324
- # @return [Boolean]
325
- def set_carried_profile(machine, toplevel)
326
- args = [
327
- 'carrier-env',
328
- '-p', machine.profile,
329
- '--set', toplevel
330
- ]
331
-
332
- MachineControl.new(machine.carrier_machine).execute!(*args).success?
333
- end
334
-
335
- # @param packages [Array<String>]
336
- # @param command [String]
337
- # @return [Boolean]
338
- def run_command_in_shell(packages: [], command: nil)
339
- args = ['nix-shell']
340
-
341
- if packages.any?
342
- args << '-p'
343
- args.concat(packages)
344
- end
345
-
346
- args << '--command'
347
- args << command
348
-
349
- cmd.run!(*args, env: { 'shellHook' => nil }).success?
350
- end
351
-
352
- # @param machine [Machine]
353
- # @yieldparam progress [NixCollectGarbage::Progress]
354
- # @return [Boolean]
355
- def collect_garbage(machine, &)
356
- gc = NixCollectGarbage.new(machine)
357
- gc.run!(&).success?
358
- end
359
-
360
- protected
361
-
362
- attr_reader :conf_dir, :show_trace, :max_jobs, :cores, :cmd
363
-
364
- # Execute block only if `out_link` does not exist or conf dir has changed
365
- # @param out_link [String] out link path
366
- def with_cache(out_link)
367
- unchanged = false
368
-
369
- if File.exist?(out_link)
370
- unchanged = ConfDir.unchanged?
371
-
372
- if unchanged
373
- cache_mtime = ConfDir.state_mtime
374
-
375
- if cache_mtime
376
- begin
377
- st = File.lstat(out_link)
378
- rescue Errno::ENOENT
379
- # pass
380
- else
381
- if st.mtime > cache_mtime
382
- Logger.instance << "Using #{out_link}\n"
383
- return
384
- end
385
- end
386
- end
387
- end
388
- end
389
-
390
- Logger.instance << "Building #{out_link}\n"
391
-
392
- unless unchanged
393
- Logger.instance << "Updating configuration cache\n"
394
- ConfDir.update_state
395
- end
396
- yield
397
- end
398
-
399
- # @param hash [Hash]
400
- # @param core_swpins [Boolean]
401
- # @yieldparam file [String]
402
- def with_argument(hash, core_swpins: false)
403
- if core_swpins
404
- paths = Swpins::Core.get.pre_evaluated_store_paths
405
- hash[:coreSwpins] = paths if paths
406
- end
407
-
408
- f = Tempfile.new('confctl')
409
- f.puts(hash.to_json)
410
- f.close
411
- yield(f.path)
412
- ensure
413
- f.unlink
414
- end
415
-
416
- def nix_instantiate(hash, **opts)
417
- with_argument(hash, **opts) do |arg|
418
- cmd_args = [
419
- 'nix-instantiate',
420
- '--eval',
421
- '--json',
422
- '--strict',
423
- '--read-write-mode',
424
- '--arg', 'jsonArg', arg,
425
- (show_trace ? '--show-trace' : nil),
426
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
427
- (cores ? ['--cores', cores.to_s] : nil),
428
- ConfCtl.nix_asset('evaluator.nix')
429
- ].flatten.compact
430
-
431
- out, = cmd.run(*cmd_args).stdout
432
-
433
- demodulify(JSON.parse(out))
434
- end
435
- end
436
-
437
- def demodulify(value)
438
- if value.is_a?(Array)
439
- value.each { |item| demodulify(item) }
440
- elsif value.is_a?(Hash)
441
- value.delete('_module')
442
- value.each_value { |v| demodulify(v) }
443
- end
444
- end
445
-
446
- def cache_dir
447
- ConfDir.cache_dir
448
- end
19
+ impl_klass = ConfigType.flake?(@conf_dir) ? NixFlake : NixLegacy
20
+ @impl = impl_klass.new(conf_dir: @conf_dir, show_trace: show_trace, max_jobs: max_jobs, cores: cores)
21
+ end
22
+
23
+ def_delegators :@impl,
24
+ :confctl_settings,
25
+ :module_options,
26
+ :list_machine_fqdns,
27
+ :list_machines,
28
+ :build_attributes,
29
+ :copy,
30
+ :activate,
31
+ :activate_with_rollback,
32
+ :set_profile,
33
+ :set_carried_profile,
34
+ :run_command_in_shell,
35
+ :collect_garbage,
36
+ :list_swpins_channels,
37
+ :eval_core_swpins,
38
+ :eval_host_swpins,
39
+ :eval_inputs_info,
40
+ :eval_inputs,
41
+ :eval_json
449
42
  end
450
43
  end