confctl 1.0.0 → 2.1.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.
- checksums.yaml +4 -4
- data/.editorconfig +1 -1
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +30 -1
- data/README.md +4 -9
- data/confctl.gemspec +14 -14
- data/docs/carrier.md +150 -0
- data/lib/confctl/cli/app.rb +19 -0
- data/lib/confctl/cli/cluster.rb +214 -49
- data/lib/confctl/cli/configuration.rb +7 -2
- data/lib/confctl/cli/gen_data.rb +19 -1
- data/lib/confctl/cli/generation.rb +47 -16
- data/lib/confctl/generation/build.rb +42 -1
- data/lib/confctl/generation/build_list.rb +10 -0
- data/lib/confctl/generation/host.rb +9 -5
- data/lib/confctl/generation/host_list.rb +22 -7
- data/lib/confctl/generation/unified.rb +5 -0
- data/lib/confctl/generation/unified_list.rb +10 -0
- data/lib/confctl/git_repo_mirror.rb +2 -2
- data/lib/confctl/machine.rb +105 -11
- data/lib/confctl/machine_control.rb +10 -2
- data/lib/confctl/machine_list.rb +18 -1
- data/lib/confctl/machine_status.rb +51 -4
- data/lib/confctl/nix.rb +90 -22
- data/lib/confctl/nix_copy.rb +5 -5
- data/lib/confctl/null_logger.rb +7 -0
- data/lib/confctl/swpins/specs/git.rb +1 -1
- data/lib/confctl/swpins/specs/git_rev.rb +1 -1
- data/lib/confctl/system_command.rb +3 -2
- data/lib/confctl/version.rb +1 -1
- data/libexec/auto-rollback.rb +106 -0
- data/man/man8/confctl-options.nix.8 +165 -1
- data/man/man8/confctl-options.nix.8.md +165 -1
- data/man/man8/confctl.8 +109 -73
- data/man/man8/confctl.8.md +86 -55
- data/nix/evaluator.nix +26 -7
- data/nix/lib/default.nix +64 -17
- data/nix/lib/machine/default.nix +14 -11
- data/nix/lib/machine/info.nix +3 -3
- data/nix/modules/cluster/default.nix +162 -3
- data/nix/modules/confctl/carrier/base.nix +35 -0
- data/nix/modules/confctl/carrier/carrier-env.rb +81 -0
- data/nix/modules/confctl/carrier/netboot/build-netboot-server.rb +962 -0
- data/nix/modules/confctl/carrier/netboot/nixos.nix +185 -0
- data/nix/modules/confctl/kexec-netboot/default.nix +36 -0
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.8.adoc +62 -0
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.rb +455 -0
- data/nix/modules/system-list.nix +10 -0
- metadata +17 -7
- data/.ruby-version +0 -1
data/lib/confctl/cli/cluster.rb
CHANGED
@@ -110,7 +110,10 @@ module ConfCtl::Cli
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def status
|
113
|
-
machines = select_machines(args[0]).managed
|
113
|
+
machines = select_machines(args[0]).managed.select do |_host, machine|
|
114
|
+
machine.target_host || machine.carried?
|
115
|
+
end
|
116
|
+
|
114
117
|
raise 'No machines to check' if machines.empty?
|
115
118
|
|
116
119
|
ask_confirmation! do
|
@@ -244,7 +247,7 @@ module ConfCtl::Cli
|
|
244
247
|
end
|
245
248
|
|
246
249
|
def test_connection
|
247
|
-
machines = select_machines_with_managed(args[0])
|
250
|
+
machines = select_machines_with_managed(args[0]).runnable
|
248
251
|
raise 'No machines to test' if machines.empty?
|
249
252
|
|
250
253
|
ask_confirmation! do
|
@@ -276,7 +279,7 @@ module ConfCtl::Cli
|
|
276
279
|
end
|
277
280
|
|
278
281
|
def ssh
|
279
|
-
machines = select_machines_with_managed(args[0])
|
282
|
+
machines = select_machines_with_managed(args[0]).runnable
|
280
283
|
raise 'No machines to ssh to' if machines.empty?
|
281
284
|
|
282
285
|
if opts['input-string'] && opts['input-file']
|
@@ -295,7 +298,7 @@ module ConfCtl::Cli
|
|
295
298
|
end
|
296
299
|
|
297
300
|
def cssh
|
298
|
-
machines = select_machines_with_managed(args[0])
|
301
|
+
machines = select_machines_with_managed(args[0]).runnable
|
299
302
|
raise 'No machines to open cssh to' if machines.empty?
|
300
303
|
|
301
304
|
ask_confirmation! do
|
@@ -330,7 +333,7 @@ module ConfCtl::Cli
|
|
330
333
|
|
331
334
|
if opts[:interactive]
|
332
335
|
host_generations.each do |host, gen|
|
333
|
-
if copy_to_host(nix, host, machines[host], gen
|
336
|
+
if copy_to_host(nix, host, machines[host], gen) == :skip
|
334
337
|
puts Rainbow("Skipping #{host}").yellow
|
335
338
|
skipped_copy << host
|
336
339
|
end
|
@@ -348,7 +351,7 @@ module ConfCtl::Cli
|
|
348
351
|
next
|
349
352
|
end
|
350
353
|
|
351
|
-
if deploy_to_host(nix, host, machines[host], gen
|
354
|
+
if deploy_to_host(nix, host, machines[host], gen, action) == :skip
|
352
355
|
puts Rainbow("Skipping #{host}").yellow
|
353
356
|
skipped_activation << host
|
354
357
|
next
|
@@ -393,14 +396,14 @@ module ConfCtl::Cli
|
|
393
396
|
host_generations.each do |host, gen|
|
394
397
|
machine = machines[host]
|
395
398
|
|
396
|
-
if copy_to_host(nix, host, machine, gen
|
399
|
+
if copy_to_host(nix, host, machine, gen) == :skip
|
397
400
|
puts Rainbow("Skipping #{host}").yellow
|
398
401
|
next
|
399
402
|
end
|
400
403
|
|
401
404
|
next if opts['copy-only']
|
402
405
|
|
403
|
-
if deploy_to_host(nix, host, machine, gen
|
406
|
+
if deploy_to_host(nix, host, machine, gen, action) == :skip
|
404
407
|
puts Rainbow("Skipping #{host}").yellow
|
405
408
|
next
|
406
409
|
end
|
@@ -418,7 +421,7 @@ module ConfCtl::Cli
|
|
418
421
|
end
|
419
422
|
end
|
420
423
|
|
421
|
-
def copy_to_host(nix, host, machine,
|
424
|
+
def copy_to_host(nix, host, machine, build_generation)
|
422
425
|
puts Rainbow("Copying configuration to #{host} (#{machine.target_host})").yellow
|
423
426
|
|
424
427
|
return :skip if opts[:interactive] && !ask_confirmation(always: true)
|
@@ -432,7 +435,7 @@ module ConfCtl::Cli
|
|
432
435
|
width: 80
|
433
436
|
)
|
434
437
|
|
435
|
-
ret = nix
|
438
|
+
ret = nix_copy(nix, machine, build_generation) do |i, n, path|
|
436
439
|
lw << "[#{i}/#{n}] #{path}"
|
437
440
|
|
438
441
|
lw.sync_console do
|
@@ -448,8 +451,10 @@ module ConfCtl::Cli
|
|
448
451
|
end
|
449
452
|
|
450
453
|
def concurrent_copy(machines, host_generations, nix)
|
454
|
+
sorted_generations = sort_generations_for_copy(machines, host_generations)
|
455
|
+
|
451
456
|
LogView.open(
|
452
|
-
header: "#{Rainbow("Copying to #{
|
457
|
+
header: "#{Rainbow("Copying to #{sorted_generations.length} machines").bright}\n",
|
453
458
|
title: Rainbow('Live view').bright
|
454
459
|
) do |lw|
|
455
460
|
multibar = TTY::ProgressBar::Multi.new(
|
@@ -458,13 +463,13 @@ module ConfCtl::Cli
|
|
458
463
|
)
|
459
464
|
executor = ConfCtl::ParallelExecutor.new(opts['max-concurrent-copy'])
|
460
465
|
|
461
|
-
|
466
|
+
sorted_generations.each do |host, gen|
|
462
467
|
pb = multibar.register(
|
463
468
|
"#{host} [:bar] :current/:total (:percent)"
|
464
469
|
)
|
465
470
|
|
466
471
|
executor.add do
|
467
|
-
ret = nix
|
472
|
+
ret = nix_copy(nix, machines[host], gen) do |i, n, path|
|
468
473
|
lw << "#{host}> [#{i}/#{n}] #{path}"
|
469
474
|
|
470
475
|
lw.sync_console do
|
@@ -501,13 +506,93 @@ module ConfCtl::Cli
|
|
501
506
|
end
|
502
507
|
end
|
503
508
|
|
504
|
-
|
509
|
+
# Sort generations for copy to target machines
|
510
|
+
#
|
511
|
+
# This algorithm interleaves carried machines from different carriers,
|
512
|
+
# so that we copy to multiple carriers at the same time, and not many carried
|
513
|
+
# machines to one carrier.
|
514
|
+
def sort_generations_for_copy(machines, host_generations)
|
515
|
+
carried, standalone = machines.to_a.partition(&:carried?)
|
516
|
+
|
517
|
+
carried_groups = carried.group_by { |m| m.carrier_machine.name }
|
518
|
+
|
519
|
+
sorted_generations = standalone.map do |m|
|
520
|
+
[m.name, host_generations[m.name]]
|
521
|
+
end
|
522
|
+
|
523
|
+
until carried_groups.empty?
|
524
|
+
carried_groups.delete_if do |_, machines|
|
525
|
+
m = machines.shift
|
526
|
+
next(true) if m.nil?
|
527
|
+
|
528
|
+
sorted_generations << [m.name, host_generations[m.name]]
|
529
|
+
|
530
|
+
machines.empty?
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
if sorted_generations.length != host_generations.length
|
535
|
+
raise 'programming error: sorted generations length != original generations'
|
536
|
+
end
|
537
|
+
|
538
|
+
sorted_generations
|
539
|
+
end
|
540
|
+
|
541
|
+
def deploy_to_host(nix, host, machine, generation, action)
|
542
|
+
ret = nil
|
543
|
+
|
505
544
|
LogView.open_with_logger(
|
506
545
|
header: "#{Rainbow('Deploying to').bright} #{Rainbow(host).yellow}\n",
|
507
546
|
title: Rainbow('Live view').bright,
|
508
547
|
size: :auto,
|
509
548
|
reserved_lines: 10
|
510
549
|
) do |lw|
|
550
|
+
ret =
|
551
|
+
if machine.carried?
|
552
|
+
deploy_carried_to_host(lw, nix, host, machine, generation, action)
|
553
|
+
else
|
554
|
+
deploy_standalone_to_host(lw, nix, host, machine, generation, action)
|
555
|
+
end
|
556
|
+
|
557
|
+
lw.flush
|
558
|
+
end
|
559
|
+
|
560
|
+
ret
|
561
|
+
end
|
562
|
+
|
563
|
+
def deploy_standalone_to_host(lw, nix, host, machine, generation, action)
|
564
|
+
return :skip if pre_host_activate(lw, nix, host, machine, generation, action) == :skip
|
565
|
+
|
566
|
+
# rubocop:disable Style/GuardClause
|
567
|
+
|
568
|
+
result =
|
569
|
+
if %w[test switch].include?(action) && enable_auto_rollback?(machine, generation)
|
570
|
+
nix.activate_with_rollback(machine, generation, action)
|
571
|
+
else
|
572
|
+
nix.activate(machine, generation, action)
|
573
|
+
end
|
574
|
+
|
575
|
+
raise "Error while activating configuration on #{host}" unless result
|
576
|
+
|
577
|
+
if %w[boot switch].include?(action) && !nix.set_profile(machine, generation.toplevel)
|
578
|
+
raise "Error while setting profile on #{host}"
|
579
|
+
end
|
580
|
+
|
581
|
+
# rubocop:enable Style/GuardClause
|
582
|
+
end
|
583
|
+
|
584
|
+
def deploy_carried_to_host(lw, nix, host, machine, generation, action)
|
585
|
+
return if action != 'switch'
|
586
|
+
|
587
|
+
# rubocop:disable Style/GuardClause
|
588
|
+
unless nix.set_carried_profile(machine, generation.toplevel)
|
589
|
+
raise "Error while setting carried profile for #{host} on #{machine.carrier_machine}"
|
590
|
+
end
|
591
|
+
# rubocop:enable Style/GuardClause
|
592
|
+
end
|
593
|
+
|
594
|
+
def pre_host_activate(lw, nix, host, machine, generation, action)
|
595
|
+
loop do
|
511
596
|
if opts['dry-activate-first']
|
512
597
|
lw.sync_console do
|
513
598
|
puts Rainbow(
|
@@ -516,7 +601,9 @@ module ConfCtl::Cli
|
|
516
601
|
).yellow
|
517
602
|
end
|
518
603
|
|
519
|
-
|
604
|
+
unless nix.activate(machine, generation, 'dry-activate')
|
605
|
+
raise "Error while activating configuration on #{host}"
|
606
|
+
end
|
520
607
|
end
|
521
608
|
|
522
609
|
lw.sync_console do
|
@@ -526,12 +613,25 @@ module ConfCtl::Cli
|
|
526
613
|
).yellow
|
527
614
|
end
|
528
615
|
|
529
|
-
return
|
616
|
+
return unless opts[:interactive]
|
617
|
+
|
618
|
+
options = {}
|
619
|
+
options['y'] = 'Continue'
|
620
|
+
options['r'] = 'Retry' if opts['dry-activate-first']
|
621
|
+
options['s'] = 'Skip'
|
622
|
+
options['a'] = 'Abort'
|
530
623
|
|
531
|
-
|
624
|
+
answer = ask_action(options:, default: nil)
|
532
625
|
|
533
|
-
|
534
|
-
|
626
|
+
case answer
|
627
|
+
when 'y'
|
628
|
+
return
|
629
|
+
when 'r'
|
630
|
+
next
|
631
|
+
when 's'
|
632
|
+
return :skip
|
633
|
+
when 'a'
|
634
|
+
raise 'Aborting'
|
535
635
|
end
|
536
636
|
end
|
537
637
|
end
|
@@ -540,6 +640,9 @@ module ConfCtl::Cli
|
|
540
640
|
if machine.localhost?
|
541
641
|
puts Rainbow("Skipping reboot of #{host} as it is localhost").yellow
|
542
642
|
return :skip
|
643
|
+
elsif machine.carried?
|
644
|
+
puts Rainbow("Skipping reboot of carried machine #{host}").yellow
|
645
|
+
return :skip
|
543
646
|
end
|
544
647
|
|
545
648
|
puts Rainbow("Rebooting #{host} (#{machine.target_host})").yellow
|
@@ -761,22 +864,16 @@ module ConfCtl::Cli
|
|
761
864
|
machines.each do |host, machine|
|
762
865
|
mc = ConfCtl::MachineControl.new(machine)
|
763
866
|
|
764
|
-
|
765
|
-
puts "#{host}:" unless aggregate
|
867
|
+
puts "#{host}:" unless aggregate
|
766
868
|
|
767
|
-
|
869
|
+
result = run_ssh_command_on_machine(mc, cmd)
|
768
870
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
if aggregate
|
776
|
-
results[host] = e
|
777
|
-
else
|
778
|
-
puts e.message
|
779
|
-
end
|
871
|
+
if aggregate
|
872
|
+
results[host] = result
|
873
|
+
elsif result.success?
|
874
|
+
puts result.out
|
875
|
+
else
|
876
|
+
puts result.err
|
780
877
|
end
|
781
878
|
|
782
879
|
puts unless aggregate
|
@@ -808,12 +905,8 @@ module ConfCtl::Cli
|
|
808
905
|
tw.add do
|
809
906
|
mc = ConfCtl::MachineControl.new(machine)
|
810
907
|
|
811
|
-
|
812
|
-
|
813
|
-
results[host] = result
|
814
|
-
rescue TTY::Command::ExitError => e
|
815
|
-
results[host] = e
|
816
|
-
end
|
908
|
+
result = run_ssh_command_on_machine(mc, cmd)
|
909
|
+
results[host] = result
|
817
910
|
|
818
911
|
lw.sync_console { pb.advance }
|
819
912
|
end
|
@@ -830,7 +923,13 @@ module ConfCtl::Cli
|
|
830
923
|
|
831
924
|
results.each do |host, result|
|
832
925
|
puts "#{host}:"
|
833
|
-
|
926
|
+
|
927
|
+
if result.success?
|
928
|
+
puts result.out
|
929
|
+
else
|
930
|
+
puts result.err
|
931
|
+
end
|
932
|
+
|
834
933
|
puts
|
835
934
|
end
|
836
935
|
end
|
@@ -844,7 +943,7 @@ module ConfCtl::Cli
|
|
844
943
|
cmd_opts[:in] = opts['input-file']
|
845
944
|
end
|
846
945
|
|
847
|
-
mc.execute(*cmd, **cmd_opts)
|
946
|
+
mc.execute!(*cmd, **cmd_opts)
|
848
947
|
end
|
849
948
|
|
850
949
|
def process_aggregated_results(results)
|
@@ -868,12 +967,15 @@ module ConfCtl::Cli
|
|
868
967
|
def find_generations(machines, generation_name)
|
869
968
|
host_generations = {}
|
870
969
|
missing_hosts = []
|
970
|
+
generation_offset = generation_name.to_i if /\A-?\d+\z/ =~ generation_name
|
871
971
|
|
872
972
|
machines.each_key do |host|
|
873
973
|
list = ConfCtl::Generation::BuildList.new(host)
|
874
974
|
|
875
975
|
gen =
|
876
|
-
if
|
976
|
+
if generation_offset
|
977
|
+
list.at_offset(generation_offset)
|
978
|
+
elsif generation_name == 'current'
|
877
979
|
list.current
|
878
980
|
else
|
879
981
|
list[generation_name]
|
@@ -888,8 +990,17 @@ module ConfCtl::Cli
|
|
888
990
|
|
889
991
|
raise 'No generation found' if host_generations.empty?
|
890
992
|
|
993
|
+
puts 'Resolved host generations:'
|
994
|
+
list_generations(host_generations, missing_hosts:)
|
995
|
+
|
996
|
+
if opts[:interactive]
|
997
|
+
ask_confirmation!
|
998
|
+
else
|
999
|
+
puts
|
1000
|
+
end
|
1001
|
+
|
891
1002
|
if missing_hosts.any?
|
892
|
-
ask_confirmation! do
|
1003
|
+
ask_confirmation!(always: opts[:interactive]) do
|
893
1004
|
puts "Generation '#{generation_name}' was not found on the following hosts:"
|
894
1005
|
missing_hosts.each { |host| puts " #{host}" }
|
895
1006
|
puts
|
@@ -900,21 +1011,59 @@ module ConfCtl::Cli
|
|
900
1011
|
host_generations
|
901
1012
|
end
|
902
1013
|
|
1014
|
+
def list_generations(host_generations, missing_hosts:)
|
1015
|
+
swpin_names = []
|
1016
|
+
|
1017
|
+
host_generations.each_value do |gen|
|
1018
|
+
gen.swpin_names.each do |name|
|
1019
|
+
swpin_names << name unless swpin_names.include?(name)
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
rows = host_generations.map do |host, gen|
|
1024
|
+
row = {
|
1025
|
+
'name' => host,
|
1026
|
+
'generation' => gen.name,
|
1027
|
+
'kernel' => gen.kernel_version
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
gen.swpin_specs.each do |name, spec|
|
1031
|
+
row[name] = spec.version
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
row
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
missing_hosts.each do |host|
|
1038
|
+
rows << { 'name' => host, 'generation' => 'not found' }
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
OutputFormatter.print(
|
1042
|
+
rows,
|
1043
|
+
%w[name generation kernel] + swpin_names,
|
1044
|
+
layout: :columns,
|
1045
|
+
sort: %w[name generation]
|
1046
|
+
)
|
1047
|
+
end
|
1048
|
+
|
903
1049
|
def do_build(machines)
|
904
1050
|
nix = ConfCtl::Nix.new(
|
905
1051
|
show_trace: opts['show-trace'],
|
906
|
-
max_jobs: opts['max-jobs']
|
1052
|
+
max_jobs: opts['max-jobs'],
|
1053
|
+
cores: opts['cores']
|
907
1054
|
)
|
908
|
-
hosts_swpin_paths = {}
|
909
1055
|
|
910
1056
|
autoupdate_swpins(machines)
|
911
1057
|
host_swpin_specs = check_swpins(machines)
|
912
1058
|
|
913
1059
|
raise 'one or more swpins need to be updated' unless host_swpin_specs
|
914
1060
|
|
915
|
-
machines.
|
916
|
-
|
917
|
-
|
1061
|
+
puts Rainbow("Evaluating swpins for #{machines.length} machines...").bright
|
1062
|
+
|
1063
|
+
hosts_swpin_paths = nix.eval_host_swpins(machines.map { |host, _| host })
|
1064
|
+
|
1065
|
+
machines.each do |host, m|
|
1066
|
+
hosts_swpin_paths[host].update(m.nix_paths)
|
918
1067
|
end
|
919
1068
|
|
920
1069
|
grps = swpin_build_groups(hosts_swpin_paths)
|
@@ -996,7 +1145,7 @@ module ConfCtl::Cli
|
|
996
1145
|
'Fetching [:bar] :current/:total (:percent)'
|
997
1146
|
)
|
998
1147
|
|
999
|
-
built_generations = nix.
|
1148
|
+
built_generations = nix.build_attributes(
|
1000
1149
|
hosts:,
|
1001
1150
|
swpin_paths:,
|
1002
1151
|
time:,
|
@@ -1244,5 +1393,21 @@ module ConfCtl::Cli
|
|
1244
1393
|
|
1245
1394
|
raise ArgumentError, "invalid time duration '#{interval}'"
|
1246
1395
|
end
|
1396
|
+
|
1397
|
+
# @param nix [Nix]
|
1398
|
+
# @param machine [Machine]
|
1399
|
+
# @param generation [Generation::Build]
|
1400
|
+
def nix_copy(nix, machine, generation, &)
|
1401
|
+
paths = [generation.toplevel]
|
1402
|
+
paths << generation.auto_rollback if enable_auto_rollback?(machine, generation)
|
1403
|
+
|
1404
|
+
nix.copy(machine, paths, &)
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
def enable_auto_rollback?(machine, generation)
|
1408
|
+
!opts['disable-auto-rollback'] \
|
1409
|
+
&& (opts['enable-auto-rollback'] || machine.auto_rollback?) \
|
1410
|
+
&& generation.auto_rollback
|
1411
|
+
end
|
1247
1412
|
end
|
1248
1413
|
end
|
@@ -1,8 +1,11 @@
|
|
1
|
+
require_relative '../hook'
|
1
2
|
require 'fileutils'
|
2
3
|
require 'securerandom'
|
3
4
|
|
4
5
|
module ConfCtl::Cli
|
5
6
|
class Configuration < Command
|
7
|
+
ConfCtl::Hook.register :configuration_rediscover
|
8
|
+
|
6
9
|
DIR_MODE = 0o755
|
7
10
|
FILE_MODE = 0o644
|
8
11
|
|
@@ -238,6 +241,8 @@ module ConfCtl::Cli
|
|
238
241
|
|
239
242
|
f.puts(']')
|
240
243
|
end
|
244
|
+
|
245
|
+
ConfCtl::Hook.call(:configuration_rediscover)
|
241
246
|
end
|
242
247
|
|
243
248
|
protected
|
@@ -251,8 +256,8 @@ module ConfCtl::Cli
|
|
251
256
|
|
252
257
|
entry_rel_path = File.join(*[rel_path, v].compact)
|
253
258
|
|
254
|
-
if File.exist?(File.join(entry_abs_path, 'module.nix'))
|
255
|
-
|
259
|
+
if File.exist?(File.join(entry_abs_path, 'module.nix')) &&
|
260
|
+
File.exist?(File.join(entry_abs_path, 'config.nix'))
|
256
261
|
ret << entry_rel_path
|
257
262
|
end
|
258
263
|
|
data/lib/confctl/cli/gen_data.rb
CHANGED
@@ -78,10 +78,28 @@ module ConfCtl::Cli
|
|
78
78
|
q.echo = false
|
79
79
|
end.to_s
|
80
80
|
|
81
|
-
@vpsadmin_client.authenticate(:
|
81
|
+
@vpsadmin_client.authenticate(:token, user:, password:, lifetime: 'fixed', interval: 60) do |_action, params|
|
82
|
+
ret = {}
|
83
|
+
|
84
|
+
params.each do |name, desc|
|
85
|
+
ret[name] = read_auth_param(name, desc)
|
86
|
+
end
|
87
|
+
|
88
|
+
ret
|
89
|
+
end
|
90
|
+
|
82
91
|
@vpsadmin_client
|
83
92
|
end
|
84
93
|
|
94
|
+
def read_auth_param(name, p)
|
95
|
+
prompt = "#{p[:label] || name}: "
|
96
|
+
|
97
|
+
ask(prompt) do |q|
|
98
|
+
q.default = nil
|
99
|
+
q.echo = !p[:protected]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
85
103
|
def update_file(relpath, &)
|
86
104
|
abs = File.join(data_dir, relpath)
|
87
105
|
tmp = "#{abs}.new"
|
@@ -11,6 +11,7 @@ module ConfCtl::Cli
|
|
11
11
|
def remove
|
12
12
|
machines = select_machines(args[0])
|
13
13
|
gens = select_generations(machines, args[1])
|
14
|
+
changed_hosts = []
|
14
15
|
|
15
16
|
if gens.empty?
|
16
17
|
puts 'No generations found'
|
@@ -27,21 +28,36 @@ module ConfCtl::Cli
|
|
27
28
|
gens.each do |gen|
|
28
29
|
puts "Removing #{gen.presence_str} generation #{gen.host}@#{gen.name}"
|
29
30
|
gen.destroy
|
31
|
+
|
32
|
+
changed_hosts << gen.host unless changed_hosts.include?(gen.host)
|
30
33
|
end
|
31
34
|
|
32
35
|
return unless opts[:remote] && opts[:gc]
|
33
36
|
|
34
|
-
machines_gc =
|
35
|
-
|
37
|
+
machines_gc = {}
|
38
|
+
|
39
|
+
machines.each do |host, machine|
|
40
|
+
next unless changed_hosts.include?(host)
|
41
|
+
|
42
|
+
m =
|
43
|
+
if machine.carried?
|
44
|
+
machine.carrier_machine
|
45
|
+
else
|
46
|
+
machine
|
47
|
+
end
|
48
|
+
|
49
|
+
machines_gc[m.name] = m if m.target_host
|
36
50
|
end
|
37
51
|
|
38
|
-
run_gc(machines_gc)
|
52
|
+
run_gc(ConfCtl::MachineList.new(machines: machines_gc))
|
39
53
|
end
|
40
54
|
|
41
55
|
def rotate
|
42
56
|
machines = select_machines(args[0])
|
43
57
|
|
44
58
|
to_delete = []
|
59
|
+
changed_hosts = []
|
60
|
+
enable_gc = opts[:remote] && opts[:gc]
|
45
61
|
|
46
62
|
to_delete.concat(host_generations_rotate(machines)) if opts[:remote]
|
47
63
|
|
@@ -56,33 +72,41 @@ module ConfCtl::Cli
|
|
56
72
|
puts 'The following generations will be removed:'
|
57
73
|
OutputFormatter.print(to_delete, %i[host name type id], layout: :columns)
|
58
74
|
puts
|
59
|
-
puts "Garbage collection: #{
|
75
|
+
puts "Garbage collection: #{enable_gc ? 'when enabled in configuration' : 'no'}"
|
60
76
|
end
|
61
77
|
|
62
78
|
to_delete.each do |gen|
|
63
79
|
puts "Removing #{gen[:type]} generation #{gen[:host]}@#{gen[:name]}"
|
64
80
|
gen[:generation].destroy
|
81
|
+
|
82
|
+
changed_hosts << gen[:host] unless changed_hosts.include?(gen[:host])
|
65
83
|
end
|
66
84
|
|
67
|
-
return unless
|
85
|
+
return unless enable_gc
|
68
86
|
|
69
87
|
global = ConfCtl::Settings.instance.host_generations
|
88
|
+
machines_gc = {}
|
89
|
+
|
90
|
+
machines.each do |host, machine|
|
91
|
+
next unless changed_hosts.include?(host)
|
70
92
|
|
71
|
-
|
72
|
-
|
93
|
+
m =
|
94
|
+
if machine.carried?
|
95
|
+
machine.carrier_machine
|
96
|
+
else
|
97
|
+
machine
|
98
|
+
end
|
73
99
|
|
74
|
-
if
|
75
|
-
|
76
|
-
|
77
|
-
gc
|
78
|
-
end
|
100
|
+
next if !m.target_host || (!m['buildGenerations']['collectGarbage'] && !global['collectGarbage'])
|
101
|
+
|
102
|
+
machines_gc[m.name] = m
|
79
103
|
end
|
80
104
|
|
81
|
-
run_gc(machines_gc) if machines_gc.any?
|
105
|
+
run_gc(ConfCtl::MachineList.new(machines: machines_gc)) if machines_gc.any?
|
82
106
|
end
|
83
107
|
|
84
108
|
def collect_garbage
|
85
|
-
machines = select_machines(args[0])
|
109
|
+
machines = select_machines(args[0]).runnable
|
86
110
|
|
87
111
|
raise 'No machines to collect garbage on' if machines.empty?
|
88
112
|
|
@@ -131,12 +155,16 @@ module ConfCtl::Cli
|
|
131
155
|
select_older_than =
|
132
156
|
(Time.now - (::Regexp.last_match(1).to_i * 24 * 60 * 60) if !select_old && /\A(\d+)d\Z/ =~ pattern)
|
133
157
|
|
158
|
+
gen_at = gens.at_offset(pattern.to_i) if /\A-?\d+\z/ =~ pattern
|
159
|
+
|
134
160
|
if pattern
|
135
161
|
gens.delete_if do |gen|
|
136
162
|
if select_old
|
137
163
|
gen.current
|
138
164
|
elsif select_older_than
|
139
165
|
gen.date >= select_older_than
|
166
|
+
elsif gen_at
|
167
|
+
gen != gen_at
|
140
168
|
else
|
141
169
|
!ConfCtl::Pattern.match?(pattern, gen.name)
|
142
170
|
end
|
@@ -295,6 +323,8 @@ module ConfCtl::Cli
|
|
295
323
|
retvals = executor.run
|
296
324
|
failed = retvals.compact
|
297
325
|
|
326
|
+
lw.flush
|
327
|
+
|
298
328
|
raise "Gargabe collection failed on: #{failed.join(', ')}" if failed.any?
|
299
329
|
end
|
300
330
|
end
|
@@ -314,7 +344,8 @@ module ConfCtl::Cli
|
|
314
344
|
'name' => gen.name,
|
315
345
|
'id' => gen.id,
|
316
346
|
'presence' => gen.presence_str,
|
317
|
-
'current' => gen.current_str
|
347
|
+
'current' => gen.current_str,
|
348
|
+
'kernel' => gen.kernel_version
|
318
349
|
}
|
319
350
|
|
320
351
|
gen.swpin_specs.each do |name, spec|
|
@@ -326,7 +357,7 @@ module ConfCtl::Cli
|
|
326
357
|
|
327
358
|
OutputFormatter.print(
|
328
359
|
rows,
|
329
|
-
%w[host name id presence current] + swpin_names,
|
360
|
+
%w[host name id presence current kernel] + swpin_names,
|
330
361
|
layout: :columns,
|
331
362
|
sort: %w[name host]
|
332
363
|
)
|