confctl 1.0.0 → 2.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.
@@ -1,26 +1,114 @@
1
1
  module ConfCtl
2
2
  class Machine
3
- attr_reader :name, :safe_name, :managed, :spin, :opts
3
+ CarriedMachine = Struct.new(
4
+ :carrier,
5
+ :name,
6
+ :alias,
7
+ :attribute,
8
+ keyword_init: true
9
+ )
10
+
11
+ # @return [String]
12
+ attr_reader :name
13
+
14
+ # @return [String]
15
+ attr_reader :safe_name
16
+
17
+ # @return [Boolean]
18
+ attr_reader :managed
19
+
20
+ # @return [String]
21
+ attr_reader :spin
22
+
23
+ # @return [String]
24
+ attr_reader :carrier_name
25
+
26
+ # @return [String]
27
+ attr_reader :cluster_name
28
+
29
+ # @return [String]
30
+ attr_reader :safe_cluster_name
31
+
32
+ # Alias for this machine on the carrier
33
+ # @return [String]
34
+ attr_reader :carried_alias
35
+
36
+ # Alias for this machine on the carrier
37
+ # @return [String]
38
+ attr_reader :safe_carried_alias
39
+
40
+ # @return [Hash] machine metadata
41
+ attr_reader :meta
4
42
 
5
43
  # @param opts [Hash]
6
- def initialize(opts)
7
- @opts = opts
44
+ # @param machine_list [MachineList]
45
+ def initialize(opts, machine_list:)
46
+ @meta = opts['metaConfig']
8
47
  @name = opts['name']
9
- @safe_name = opts['name'].gsub('/', ':')
10
- @managed = opts['managed']
11
- @spin = opts['spin']
48
+ @safe_name = name.gsub('/', ':')
49
+ @managed = meta['managed']
50
+ @spin = meta['spin']
51
+ @is_carrier = meta.fetch('carrier', {}).fetch('enable', false)
52
+ @carrier_name = opts['carrier']
53
+ @cluster_name = opts['clusterName']
54
+ @safe_cluster_name = cluster_name.gsub('/', ':')
55
+ @carried_alias = opts['alias'] || @cluster_name
56
+ @safe_carried_alias = @carried_alias.gsub('/', ':')
57
+ @machine_list = machine_list
58
+ end
59
+
60
+ # True if this machine carries other machines
61
+ def carrier?
62
+ @is_carrier
63
+ end
64
+
65
+ # @return [Array<CarriedMachine>]
66
+ def carried_machines
67
+ meta.fetch('carrier', {}).fetch('machines', []).map do |m|
68
+ CarriedMachine.new(
69
+ carrier: self,
70
+ name: m['machine'],
71
+ alias: m['alias'] || m['machine'],
72
+ attribute: m['attribute']
73
+ )
74
+ end
75
+ end
76
+
77
+ # True if this machine is on a carrier
78
+ def carried?
79
+ !@carrier_name.nil?
80
+ end
81
+
82
+ # @return [Machine] carrier
83
+ def carrier_machine
84
+ carrier = @machine_list[@carrier_name]
85
+
86
+ if carrier.nil?
87
+ raise "Carrier #{@carrier_name} not found in machine list"
88
+ end
89
+
90
+ carrier
12
91
  end
13
92
 
14
93
  def target_host
15
- (opts['host'] && opts['host']['target']) || name
94
+ meta.fetch('host', {}).fetch('target', name)
16
95
  end
17
96
 
18
97
  def localhost?
19
98
  target_host == 'localhost'
20
99
  end
21
100
 
101
+ # @return [String] path to nix-env managed profile
102
+ def profile
103
+ if carried?
104
+ "/nix/var/nix/profiles/confctl-#{safe_carried_alias}"
105
+ else
106
+ '/nix/var/nix/profiles/system'
107
+ end
108
+ end
109
+
22
110
  def nix_paths
23
- opts['nix']['nixPath'].to_h do |v|
111
+ meta['nix']['nixPath'].to_h do |v|
24
112
  eq = v.index('=')
25
113
  raise "'#{v}' is not a valid nix path entry " if eq.nil?
26
114
 
@@ -33,7 +121,7 @@ module ConfCtl
33
121
 
34
122
  @health_checks = []
35
123
 
36
- opts['healthChecks'].each do |type, checks|
124
+ meta['healthChecks'].each do |type, checks|
37
125
  case type
38
126
  when 'systemd'
39
127
  next if !checks['enable'] || spin != 'nixos'
@@ -76,11 +164,13 @@ module ConfCtl
76
164
 
77
165
  def [](key)
78
166
  if key.index('.')
79
- get(opts, key.split('.'))
167
+ get(meta, key.split('.'))
168
+ elsif key == 'name'
169
+ name
80
170
  elsif key == 'checks'
81
171
  health_checks.length
82
172
  else
83
- opts[key]
173
+ meta[key]
84
174
  end
85
175
  end
86
176
 
@@ -108,6 +108,13 @@ module ConfCtl
108
108
  out.strip
109
109
  end
110
110
 
111
+ # @param path [String]
112
+ # @return [String]
113
+ def read_realpath(path)
114
+ out, = run_cmd('realpath', path)
115
+ out.strip
116
+ end
117
+
111
118
  # Execute command, raises exception on error
112
119
  # @yieldparam out [String]
113
120
  # @yieldparam err [String]
@@ -46,6 +46,12 @@ module ConfCtl
46
46
  machines.map(&)
47
47
  end
48
48
 
49
+ # @yieldparam [Machine] machine
50
+ # @return [Hash]
51
+ def transform_values(&)
52
+ machines.transform_values(&)
53
+ end
54
+
49
55
  # @return [MachineList]
50
56
  def managed
51
57
  select { |_host, machine| machine.managed }
@@ -56,6 +62,11 @@ module ConfCtl
56
62
  select { |_host, machine| !machine.managed }
57
63
  end
58
64
 
65
+ # @return [MachineList]
66
+ def runnable
67
+ select { |_host, machine| !machine.carried? && machine.target_host }
68
+ end
69
+
59
70
  # @param host [String]
60
71
  def [](host)
61
72
  @machines[host]
@@ -84,6 +95,8 @@ module ConfCtl
84
95
  checks = []
85
96
 
86
97
  machines.each_value do |machine|
98
+ next if machine.carried?
99
+
87
100
  checks.concat(machine.health_checks)
88
101
  end
89
102
 
@@ -101,7 +114,7 @@ module ConfCtl
101
114
 
102
115
  def parse(data)
103
116
  data.transform_values do |info|
104
- Machine.new(info)
117
+ Machine.new(info, machine_list: self)
105
118
  end
106
119
  end
107
120
  end
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
  require 'tty-command'
2
3
 
3
4
  module ConfCtl
@@ -81,7 +82,7 @@ module ConfCtl
81
82
  # @param machine [Machine]
82
83
  def initialize(machine)
83
84
  @machine = machine
84
- @mc = MachineControl.new(machine)
85
+ @mc = MachineControl.new(machine.carried? ? machine.carrier_machine : machine)
85
86
  end
86
87
 
87
88
  # Connect to the machine and query its state
@@ -94,7 +95,7 @@ module ConfCtl
94
95
 
95
96
  if toplevel
96
97
  begin
97
- @current_toplevel = mc.read_symlink('/run/current-system')
98
+ @current_toplevel = query_toplevel
98
99
  rescue TTY::Command::ExitError
99
100
  return
100
101
  end
@@ -102,14 +103,14 @@ module ConfCtl
102
103
 
103
104
  if generations
104
105
  begin
105
- @generations = Generation::HostList.fetch(mc)
106
+ @generations = Generation::HostList.fetch(mc, profile: machine.profile)
106
107
  rescue TTY::Command::ExitError
107
108
  return
108
109
  end
109
110
  end
110
111
 
111
112
  begin
112
- @swpins_info = Swpins::DeployedInfo.parse!(mc.read_file('/etc/confctl/swpins-info.json'))
113
+ @swpins_info = query_swpins
113
114
  rescue Error
114
115
  nil
115
116
  end
@@ -131,5 +132,51 @@ module ConfCtl
131
132
  protected
132
133
 
133
134
  attr_reader :mc
135
+
136
+ def query_toplevel
137
+ path =
138
+ if machine.carried?
139
+ machine.profile
140
+ else
141
+ '/run/current-system'
142
+ end
143
+
144
+ mc.read_realpath(path)
145
+ end
146
+
147
+ 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
154
+
155
+ case json
156
+ when String
157
+ Swpins::DeployedInfo.parse!(json)
158
+ when Hash
159
+ json
160
+ end
161
+ end
162
+
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
171
+ end
172
+
173
+ begin
174
+ return mc.read_file(File.join(machine.profile, '/etc/confctl/swpins-info.json'))
175
+ rescue TTY::Command::ExitError
176
+ # pass
177
+ end
178
+
179
+ nil
180
+ end
134
181
  end
135
182
  end
data/lib/confctl/nix.rb CHANGED
@@ -15,10 +15,11 @@ module ConfCtl
15
15
 
16
16
  include Utils::File
17
17
 
18
- def initialize(conf_dir: nil, show_trace: false, max_jobs: nil)
18
+ def initialize(conf_dir: nil, show_trace: false, max_jobs: nil, cores: nil)
19
19
  @conf_dir = conf_dir || ConfDir.path
20
20
  @show_trace = show_trace
21
21
  @max_jobs = max_jobs || Settings.instance.max_jobs
22
+ @cores = cores
22
23
  @cmd = SystemCommand.new
23
24
  end
24
25
 
@@ -40,6 +41,7 @@ module ConfCtl
40
41
  '--out-link', out_link,
41
42
  (show_trace ? '--show-trace' : nil),
42
43
  (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
44
+ (cores ? ['--cores', cores.to_s] : nil),
43
45
  ConfCtl.nix_asset('evaluator.nix')
44
46
  ].flatten.compact
45
47
 
@@ -88,6 +90,7 @@ module ConfCtl
88
90
  '--out-link', out_link,
89
91
  (show_trace ? '--show-trace' : nil),
90
92
  (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
93
+ (cores ? ['--cores', cores.to_s] : nil),
91
94
  ConfCtl.nix_asset('evaluator.nix')
92
95
  ].flatten.compact
93
96
 
@@ -124,6 +127,7 @@ module ConfCtl
124
127
  '--out-link', out_link,
125
128
  (show_trace ? '--show-trace' : nil),
126
129
  (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
130
+ (cores ? ['--cores', cores.to_s] : nil),
127
131
  ConfCtl.nix_asset('evaluator.nix')
128
132
  ].flatten.compact
129
133
 
@@ -154,6 +158,7 @@ module ConfCtl
154
158
  '--out-link', out_link,
155
159
  (show_trace ? '--show-trace' : nil),
156
160
  (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
161
+ (cores ? ['--cores', cores.to_s] : nil),
157
162
  ConfCtl.nix_asset('evaluator.nix')
158
163
  ].flatten.compact
159
164
 
@@ -165,7 +170,7 @@ module ConfCtl
165
170
 
166
171
  # Build config.system.build.toplevel for selected hosts
167
172
  #
168
- # @param hosts [Array<String>]
173
+ # @param hosts [Array<Machine>]
169
174
  # @param swpin_paths [Hash]
170
175
  # @param host_swpin_specs [Hash]
171
176
  # @param time [Time]
@@ -176,7 +181,7 @@ module ConfCtl
176
181
  # @yieldparam path [String]
177
182
  #
178
183
  # @return [Hash<String, Generation::Build>]
179
- def build_toplevels(hosts: [], swpin_paths: {}, host_swpin_specs: {}, time: nil, &block)
184
+ def build_attributes(hosts: [], swpin_paths: {}, host_swpin_specs: {}, time: nil, &block)
180
185
  with_argument({
181
186
  confDir: conf_dir,
182
187
  build: :toplevel,
@@ -191,6 +196,7 @@ module ConfCtl
191
196
  '--out-link', out_link,
192
197
  (show_trace ? '--show-trace' : nil),
193
198
  (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
199
+ (cores ? ['--cores', cores.to_s] : nil),
194
200
  ConfCtl.nix_asset('evaluator.nix')
195
201
  ].flatten.compact
196
202
 
@@ -236,6 +242,9 @@ module ConfCtl
236
242
  def copy(machine, toplevel, &)
237
243
  if machine.localhost?
238
244
  true
245
+ elsif machine.carried?
246
+ cp = NixCopy.new(machine.carrier_machine.target_host, toplevel)
247
+ cp.run!(&).success?
239
248
  else
240
249
  cp = NixCopy.new(machine.target_host, toplevel)
241
250
  cp.run!(&).success?
@@ -258,13 +267,26 @@ module ConfCtl
258
267
  def set_profile(machine, toplevel)
259
268
  args = [
260
269
  'nix-env',
261
- '-p', '/nix/var/nix/profiles/system',
270
+ '-p', machine.profile,
262
271
  '--set', toplevel
263
272
  ]
264
273
 
265
274
  MachineControl.new(machine).execute!(*args).success?
266
275
  end
267
276
 
277
+ # @param machine [Machine]
278
+ # @param toplevel [String]
279
+ # @return [Boolean]
280
+ def set_carried_profile(machine, toplevel)
281
+ args = [
282
+ 'carrier-env',
283
+ '-p', machine.profile,
284
+ '--set', toplevel
285
+ ]
286
+
287
+ MachineControl.new(machine.carrier_machine).execute!(*args).success?
288
+ end
289
+
268
290
  # @param packages [Array<String>]
269
291
  # @param command [String]
270
292
  # @return [Boolean]
@@ -292,7 +314,7 @@ module ConfCtl
292
314
 
293
315
  protected
294
316
 
295
- attr_reader :conf_dir, :show_trace, :max_jobs, :cmd
317
+ attr_reader :conf_dir, :show_trace, :max_jobs, :cores, :cmd
296
318
 
297
319
  # Execute block only if `out_link` does not exist or conf dir has changed
298
320
  # @param out_link [String] out link path
@@ -357,6 +379,7 @@ module ConfCtl
357
379
  '--arg', 'jsonArg', arg,
358
380
  (show_trace ? '--show-trace' : nil),
359
381
  (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
382
+ (cores ? ['--cores', cores.to_s] : nil),
360
383
  ConfCtl.nix_asset('evaluator.nix')
361
384
  ].flatten.compact
362
385
 
@@ -103,7 +103,7 @@ module ConfCtl
103
103
 
104
104
  def prefetch_github(ref)
105
105
  mirror = GitRepoMirror.new(nix_opts['url'])
106
- mirror.setup
106
+ mirror.setup(ref:)
107
107
 
108
108
  rev = mirror.revision_parse(ref)
109
109
  url = File.join(nix_opts['url'], 'archive', "#{rev}.tar.gz")
@@ -1,3 +1,3 @@
1
1
  module ConfCtl
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
@@ -1,4 +1,4 @@
1
- .TH confctl\-options.nix 8 2024\-02\-17 master
1
+ .TH confctl\-options.nix 8 2024\-05\-07 master
2
2
  .SH NAME
3
3
  .PP
4
4
  \fB\fCconfctl\-options.nix\fR \- confctl configuration documentation
@@ -599,6 +599,22 @@ Address with prefix as string
599
599
  .PP
600
600
  \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
601
601
  .TP
602
+ \fB\fCcluster.<name>.buildAttribute\fR
603
+ Path to the attribute in machine system config that should be built
604
+ .IP
605
+ For example, \fB\fC[ "system" "build" "toplevel" ]\fR will select attribute
606
+ \fB\fCconfig.system.build.toplevel\fR\&.
607
+ .PP
608
+ \fIType:\fP list of string
609
+ .PP
610
+ \fIDefault:\fP \fB\fC[
611
+ "system"
612
+ "build"
613
+ "toplevel"
614
+ ]\fR
615
+ .PP
616
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
617
+ .TP
602
618
  \fB\fCcluster.<name>.buildGenerations.max\fR
603
619
  The maximum number of build generations to be kept on the build
604
620
  machine.
@@ -632,6 +648,151 @@ machine.
632
648
  .PP
633
649
  \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
634
650
  .TP
651
+ \fB\fCcluster.<name>.carrier.enable\fR
652
+ Whether to enable This machine is a carrier for other machines.
653
+ .PP
654
+ \fIType:\fP boolean
655
+ .PP
656
+ \fIDefault:\fP \fB\fCfalse\fR
657
+ .PP
658
+ \fIExample:\fP \fB\fCtrue\fR
659
+ .PP
660
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
661
+ .TP
662
+ \fB\fCcluster.<name>.carrier.machines\fR
663
+ List of carried machines
664
+ .PP
665
+ \fIType:\fP list of (submodule)
666
+ .PP
667
+ \fIDefault:\fP \fB\fC[ ]\fR
668
+ .PP
669
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
670
+ .TP
671
+ \fB\fCcluster.<name>.carrier.machines.*.alias\fR
672
+ Alias for carried machine name
673
+ .PP
674
+ \fIType:\fP null or string
675
+ .PP
676
+ \fIDefault:\fP \fB\fCnull\fR
677
+ .PP
678
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
679
+ .TP
680
+ \fB\fCcluster.<name>.carrier.machines.*.buildAttribute\fR
681
+ Path to the attribute in machine system config that should be built
682
+ .IP
683
+ For example, \fB\fC[ "system" "build" "toplevel" ]\fR will select attribute
684
+ \fB\fCconfig.system.build.toplevel\fR\&.
685
+ .PP
686
+ \fIType:\fP list of string
687
+ .PP
688
+ \fIDefault:\fP \fB\fC[
689
+ "system"
690
+ "build"
691
+ "toplevel"
692
+ ]\fR
693
+ .PP
694
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
695
+ .TP
696
+ \fB\fCcluster.<name>.carrier.machines.*.buildGenerations.max\fR
697
+ The maximum number of build generations to be kept on the build
698
+ machine.
699
+ .PP
700
+ \fIType:\fP null or signed integer
701
+ .PP
702
+ \fIDefault:\fP \fB\fCnull\fR
703
+ .PP
704
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
705
+ .TP
706
+ \fB\fCcluster.<name>.carrier.machines.*.buildGenerations.maxAge\fR
707
+ Delete build generations older than
708
+ \fB\fCcluster.<name>.carrier.machines.*.buildGenerations.maxAge\fR
709
+ seconds from the build machine. Old generations are deleted even
710
+ if \fB\fCcluster.<name>.carrier.machines.*.buildGenerations.max\fR is
711
+ not reached.
712
+ .PP
713
+ \fIType:\fP null or signed integer
714
+ .PP
715
+ \fIDefault:\fP \fB\fCnull\fR
716
+ .PP
717
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
718
+ .TP
719
+ \fB\fCcluster.<name>.carrier.machines.*.buildGenerations.min\fR
720
+ The minimum number of build generations to be kept on the build
721
+ machine.
722
+ .PP
723
+ \fIType:\fP null or signed integer
724
+ .PP
725
+ \fIDefault:\fP \fB\fCnull\fR
726
+ .PP
727
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
728
+ .TP
729
+ \fB\fCcluster.<name>.carrier.machines.*.extraModules\fR
730
+ A list of additional NixOS modules to be imported for this machine
731
+ .PP
732
+ \fIType:\fP list of path
733
+ .PP
734
+ \fIDefault:\fP \fB\fC[ ]\fR
735
+ .PP
736
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
737
+ .TP
738
+ \fB\fCcluster.<name>.carrier.machines.*.hostGenerations.max\fR
739
+ The maximum number of generations to be kept on the machine.
740
+ .PP
741
+ \fIType:\fP null or signed integer
742
+ .PP
743
+ \fIDefault:\fP \fB\fCnull\fR
744
+ .PP
745
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
746
+ .TP
747
+ \fB\fCcluster.<name>.carrier.machines.*.hostGenerations.maxAge\fR
748
+ Delete generations older than
749
+ \fB\fCcluster.<name>.carrier.machines.*.hostGenerations.maxAge\fR
750
+ seconds from the machine. Old generations are deleted even
751
+ if \fB\fCcluster.<name>.carrier.machines.*.hostGenerations.max\fR is
752
+ not reached.
753
+ .PP
754
+ \fIType:\fP null or signed integer
755
+ .PP
756
+ \fIDefault:\fP \fB\fCnull\fR
757
+ .PP
758
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
759
+ .TP
760
+ \fB\fCcluster.<name>.carrier.machines.*.hostGenerations.min\fR
761
+ The minimum number of generations to be kept on the machine.
762
+ .PP
763
+ \fIType:\fP null or signed integer
764
+ .PP
765
+ \fIDefault:\fP \fB\fCnull\fR
766
+ .PP
767
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
768
+ .TP
769
+ \fB\fCcluster.<name>.carrier.machines.*.labels\fR
770
+ Optional user\-defined labels to classify the machine
771
+ .PP
772
+ \fIType:\fP attribute set
773
+ .PP
774
+ \fIDefault:\fP \fB\fC{ }\fR
775
+ .PP
776
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
777
+ .TP
778
+ \fB\fCcluster.<name>.carrier.machines.*.machine\fR
779
+ Machine name
780
+ .PP
781
+ \fIType:\fP string
782
+ .PP
783
+ \fIDefault:\fP \fB\fCnull\fR
784
+ .PP
785
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
786
+ .TP
787
+ \fB\fCcluster.<name>.carrier.machines.*.tags\fR
788
+ Optional user\-defined tags to classify the machine
789
+ .PP
790
+ \fIType:\fP list of string
791
+ .PP
792
+ \fIDefault:\fP \fB\fC[ ]\fR
793
+ .PP
794
+ \fIDeclared by:\fP \fB\fC<confctl/nix/modules/cluster>\fR
795
+ .TP
635
796
  \fB\fCcluster.<name>.healthChecks.builderCommands\fR
636
797
  Check commands run on the build machine
637
798
  .PP
@@ -1032,6 +1193,9 @@ Host name
1032
1193
  .TP
1033
1194
  \fB\fCcluster.<name>.host.target\fR
1034
1195
  Address/host to which the configuration is deployed to
1196
+ .IP
1197
+ Set to null if the machine is not deployable, e.g. when it is only used
1198
+ as a carried machine.
1035
1199
  .PP
1036
1200
  \fIType:\fP null or string
1037
1201
  .PP