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
@@ -16,6 +16,9 @@ module ConfCtl
|
|
16
16
|
# @return [String]
|
17
17
|
attr_reader :toplevel
|
18
18
|
|
19
|
+
# @return [String]
|
20
|
+
attr_reader :auto_rollback
|
21
|
+
|
19
22
|
# @return [Array<String>]
|
20
23
|
attr_reader :swpin_names
|
21
24
|
|
@@ -29,22 +32,28 @@ module ConfCtl
|
|
29
32
|
# @return [Boolean]
|
30
33
|
attr_accessor :current
|
31
34
|
|
35
|
+
# @return [String, nil]
|
36
|
+
attr_reader :kernel_version
|
37
|
+
|
32
38
|
# @param host [String]
|
33
39
|
def initialize(host)
|
34
40
|
@host = host
|
35
41
|
end
|
36
42
|
|
37
43
|
# @param toplevel [String]
|
44
|
+
# @param auto_rollback [String]
|
38
45
|
# @param swpin_paths [Hash]
|
39
46
|
# @param swpin_specs [Hash]
|
40
47
|
# @param date [Time]
|
41
|
-
def create(toplevel, swpin_paths, swpin_specs, date: nil)
|
48
|
+
def create(toplevel, auto_rollback, swpin_paths, swpin_specs, date: nil)
|
42
49
|
@toplevel = toplevel
|
50
|
+
@auto_rollback = auto_rollback
|
43
51
|
@swpin_names = swpin_paths.keys
|
44
52
|
@swpin_paths = swpin_paths
|
45
53
|
@swpin_specs = swpin_specs
|
46
54
|
@date = date || Time.now
|
47
55
|
@name = date.strftime('%Y-%m-%d--%H-%M-%S')
|
56
|
+
@kernel_version = extract_kernel_version
|
48
57
|
end
|
49
58
|
|
50
59
|
# @param name [String]
|
@@ -53,6 +62,7 @@ module ConfCtl
|
|
53
62
|
|
54
63
|
cfg = JSON.parse(File.read(config_path))
|
55
64
|
@toplevel = cfg['toplevel']
|
65
|
+
@auto_rollback = cfg['auto_rollback']
|
56
66
|
|
57
67
|
@swpin_names = []
|
58
68
|
@swpin_paths = {}
|
@@ -69,6 +79,7 @@ module ConfCtl
|
|
69
79
|
end
|
70
80
|
|
71
81
|
@date = Time.iso8601(cfg['date'])
|
82
|
+
@kernel_version = extract_kernel_version
|
72
83
|
rescue StandardError => e
|
73
84
|
raise Error, "invalid generation '#{name}': #{e.message}"
|
74
85
|
end
|
@@ -76,6 +87,7 @@ module ConfCtl
|
|
76
87
|
def save
|
77
88
|
FileUtils.mkdir_p(dir)
|
78
89
|
File.symlink(toplevel, toplevel_path)
|
90
|
+
File.symlink(auto_rollback, auto_rollback_path)
|
79
91
|
|
80
92
|
swpin_paths.each do |name, path|
|
81
93
|
File.symlink(path, swpin_path(name))
|
@@ -85,6 +97,7 @@ module ConfCtl
|
|
85
97
|
f.puts(JSON.pretty_generate({
|
86
98
|
date: date.iso8601,
|
87
99
|
toplevel:,
|
100
|
+
auto_rollback:,
|
88
101
|
swpins: swpin_paths.to_h do |name, path|
|
89
102
|
[name, { path:, spec: swpin_specs[name].as_json }]
|
90
103
|
end
|
@@ -97,6 +110,13 @@ module ConfCtl
|
|
97
110
|
def destroy
|
98
111
|
remove_gcroot
|
99
112
|
File.unlink(toplevel_path)
|
113
|
+
|
114
|
+
begin
|
115
|
+
File.unlink(auto_rollback_path)
|
116
|
+
rescue Errno::ENOENT
|
117
|
+
# Older generations might not have auto_rollback
|
118
|
+
end
|
119
|
+
|
100
120
|
swpin_paths.each_key { |name| File.unlink(swpin_path(name)) }
|
101
121
|
File.unlink(config_path)
|
102
122
|
Dir.rmdir(dir)
|
@@ -104,6 +124,7 @@ module ConfCtl
|
|
104
124
|
|
105
125
|
def add_gcroot
|
106
126
|
GCRoot.add(gcroot_name('toplevel'), toplevel_path)
|
127
|
+
GCRoot.add(gcroot_name('auto_rollback'), auto_rollback_path)
|
107
128
|
swpin_paths.each_key do |name|
|
108
129
|
GCRoot.add(gcroot_name("swpin.#{name}"), toplevel_path)
|
109
130
|
end
|
@@ -111,6 +132,7 @@ module ConfCtl
|
|
111
132
|
|
112
133
|
def remove_gcroot
|
113
134
|
GCRoot.remove(gcroot_name('toplevel'))
|
135
|
+
GCRoot.remove(gcroot_name('auto_rollback'))
|
114
136
|
swpin_paths.each_key do |name|
|
115
137
|
GCRoot.remove(gcroot_name("swpin.#{name}"))
|
116
138
|
end
|
@@ -130,6 +152,10 @@ module ConfCtl
|
|
130
152
|
@toplevel_path ||= File.join(dir, 'toplevel')
|
131
153
|
end
|
132
154
|
|
155
|
+
def auto_rollback_path
|
156
|
+
@auto_rollback_path ||= File.join(dir, 'auto_rollback')
|
157
|
+
end
|
158
|
+
|
133
159
|
def swpin_path(name)
|
134
160
|
File.join(dir, "#{name}.swpin")
|
135
161
|
end
|
@@ -141,5 +167,20 @@ module ConfCtl
|
|
141
167
|
def gcroot_name(file)
|
142
168
|
"#{escaped_host}-generation-#{name}-#{file}"
|
143
169
|
end
|
170
|
+
|
171
|
+
def extract_kernel_version
|
172
|
+
# `kernel` is for NixOS/vpsAdminOS and also carried NixOS machines (netboot)
|
173
|
+
# `bzImage` is for carried vpsAdminOS machines (netboot)
|
174
|
+
%w[kernel bzImage].each do |v|
|
175
|
+
link = File.readlink(File.join(toplevel, v))
|
176
|
+
next unless %r{\A/nix/store/[^-]+-linux-([^/]+)} =~ link
|
177
|
+
|
178
|
+
return ::Regexp.last_match(1)
|
179
|
+
rescue Errno::ENOENT, Errno::EINVAL
|
180
|
+
next
|
181
|
+
end
|
182
|
+
|
183
|
+
nil
|
184
|
+
end
|
144
185
|
end
|
145
186
|
end
|
@@ -55,6 +55,16 @@ module ConfCtl
|
|
55
55
|
index[name]
|
56
56
|
end
|
57
57
|
|
58
|
+
# @param offset [Integer] 0 = current/last, 1 = first (oldest), -1 = before last
|
59
|
+
# @return [Generation::Build]
|
60
|
+
def at_offset(offset)
|
61
|
+
if offset == 0
|
62
|
+
generations.last
|
63
|
+
else
|
64
|
+
generations[offset - 1]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
58
68
|
def each(&)
|
59
69
|
generations.each(&)
|
60
70
|
end
|
@@ -1,19 +1,22 @@
|
|
1
1
|
module ConfCtl
|
2
2
|
class Generation::Host
|
3
|
-
attr_reader :host, :profile, :id, :toplevel, :date, :current
|
3
|
+
attr_reader :host, :profile, :id, :toplevel, :date, :kernel_version, :current
|
4
4
|
|
5
|
-
# @param
|
5
|
+
# @param machine [Machine]
|
6
6
|
# @param profile [String]
|
7
7
|
# @param id [Integer]
|
8
8
|
# @param toplevel [String]
|
9
9
|
# @param date [Time]
|
10
|
+
# @param kernel_version [String, nil]
|
10
11
|
# @param mc [MachineControl]
|
11
|
-
def initialize(
|
12
|
-
@host =
|
12
|
+
def initialize(machine, profile, id, toplevel, date, kernel_version, current: false, mc: nil)
|
13
|
+
@host = machine.name
|
14
|
+
@machine = machine
|
13
15
|
@profile = profile
|
14
16
|
@id = id
|
15
17
|
@toplevel = toplevel
|
16
18
|
@date = date
|
19
|
+
@kernel_version = kernel_version
|
17
20
|
@current = current
|
18
21
|
@mc = mc
|
19
22
|
end
|
@@ -25,7 +28,8 @@ module ConfCtl
|
|
25
28
|
def destroy
|
26
29
|
raise 'machine control not available' if mc.nil?
|
27
30
|
|
28
|
-
|
31
|
+
env_cmd = @machine.carried? ? 'carrier-env' : 'nix-env'
|
32
|
+
mc.execute(env_cmd, '-p', profile, '--delete-generations', id.to_s)
|
29
33
|
end
|
30
34
|
|
31
35
|
protected
|
@@ -1,24 +1,33 @@
|
|
1
1
|
module ConfCtl
|
2
2
|
class Generation::HostList
|
3
|
+
# @parma machine [Machine]
|
3
4
|
# @param mc [MachineControl]
|
5
|
+
# @param profile [String]
|
4
6
|
# @return [Generation::HostList]
|
5
|
-
def self.fetch(mc, profile:
|
6
|
-
out, = mc.bash_script(
|
7
|
+
def self.fetch(machine, mc, profile:)
|
8
|
+
out, = mc.bash_script(<<~END)
|
7
9
|
realpath #{profile}
|
8
10
|
|
9
11
|
for generation in `ls -d -1 #{profile}-*-link` ; do
|
10
|
-
echo "$generation
|
12
|
+
echo -n "$generation;"
|
13
|
+
echo -n "$(readlink $generation);"
|
14
|
+
echo -n "$(stat --format=%Y $generation);"
|
15
|
+
|
16
|
+
for kernel_file in kernel bzImage ; do
|
17
|
+
[ -h "$generation/$kernel_file" ] && echo -n $(readlink "$generation/$kernel_file")
|
18
|
+
done
|
19
|
+
|
20
|
+
echo
|
11
21
|
done
|
12
22
|
END
|
13
|
-
)
|
14
23
|
|
15
|
-
list = new(
|
24
|
+
list = new(machine.name)
|
16
25
|
lines = out.strip.split("\n")
|
17
26
|
current_path = lines.shift
|
18
27
|
id_rx = /^#{Regexp.escape(profile)}-(\d+)-link$/
|
19
28
|
|
20
29
|
lines.each do |line|
|
21
|
-
link, path, created_at = line.split(';')
|
30
|
+
link, path, created_at, kernel = line.split(';')
|
22
31
|
|
23
32
|
if id_rx =~ link
|
24
33
|
id = ::Regexp.last_match(1).to_i
|
@@ -27,12 +36,18 @@ module ConfCtl
|
|
27
36
|
next
|
28
37
|
end
|
29
38
|
|
39
|
+
kernel_version =
|
40
|
+
if kernel && %r{\A/nix/store/[^-]+-linux-([^/]+)} =~ kernel
|
41
|
+
::Regexp.last_match(1)
|
42
|
+
end
|
43
|
+
|
30
44
|
list << Generation::Host.new(
|
31
|
-
|
45
|
+
machine,
|
32
46
|
profile,
|
33
47
|
id,
|
34
48
|
path,
|
35
49
|
Time.at(created_at.to_i),
|
50
|
+
kernel_version,
|
36
51
|
current: path == current_path,
|
37
52
|
mc:
|
38
53
|
)
|
@@ -15,6 +15,9 @@ module ConfCtl
|
|
15
15
|
# @return [Time]
|
16
16
|
attr_reader :date
|
17
17
|
|
18
|
+
# @return [String, nil]
|
19
|
+
attr_reader :kernel_version
|
20
|
+
|
18
21
|
# @return [Boolean]
|
19
22
|
attr_reader :current
|
20
23
|
|
@@ -37,11 +40,13 @@ module ConfCtl
|
|
37
40
|
@name = build_generation.name
|
38
41
|
@toplevel = build_generation.toplevel
|
39
42
|
@date = build_generation.date
|
43
|
+
@kernel_version = build_generation.kernel_version
|
40
44
|
@current ||= build_generation.current
|
41
45
|
elsif host_generation
|
42
46
|
@name = host_generation.approx_name
|
43
47
|
@toplevel = host_generation.toplevel
|
44
48
|
@date = host_generation.date
|
49
|
+
@kernel_version = host_generation.kernel_version
|
45
50
|
@current ||= host_generation.current
|
46
51
|
else
|
47
52
|
raise ArgumentError, 'set build or host'
|
@@ -46,6 +46,16 @@ module ConfCtl
|
|
46
46
|
generations.each(&)
|
47
47
|
end
|
48
48
|
|
49
|
+
# @param offset [Integer] 0 = current/last, 1 = first (oldest), -1 = before last
|
50
|
+
# @return [Generation::Unified]
|
51
|
+
def at_offset(offset)
|
52
|
+
if offset == 0
|
53
|
+
generations.last
|
54
|
+
else
|
55
|
+
generations[offset - 1]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
49
59
|
def delete_if(&)
|
50
60
|
generations.delete_if(&)
|
51
61
|
end
|
@@ -14,13 +14,13 @@ module ConfCtl
|
|
14
14
|
@cmd = SystemCommand.new
|
15
15
|
end
|
16
16
|
|
17
|
-
def setup
|
17
|
+
def setup(ref: nil)
|
18
18
|
File.stat(mirror_path)
|
19
19
|
rescue Errno::ENOENT
|
20
20
|
FileUtils.mkdir_p(mirror_path)
|
21
21
|
git('clone', args: ['--mirror', url, mirror_path])
|
22
22
|
else
|
23
|
-
git_repo('fetch')
|
23
|
+
git_repo('fetch', opts: ['--no-show-forced-updates'], args: (ref ? ['origin', ref] : []))
|
24
24
|
end
|
25
25
|
|
26
26
|
def revision_parse(str)
|
data/lib/confctl/machine.rb
CHANGED
@@ -1,26 +1,114 @@
|
|
1
1
|
module ConfCtl
|
2
2
|
class Machine
|
3
|
-
|
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
|
-
|
7
|
-
|
44
|
+
# @param machine_list [MachineList]
|
45
|
+
def initialize(opts, machine_list:)
|
46
|
+
@meta = opts['metaConfig']
|
8
47
|
@name = opts['name']
|
9
|
-
@safe_name =
|
10
|
-
@managed =
|
11
|
-
@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
|
-
(
|
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
|
-
|
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
|
|
@@ -28,12 +116,16 @@ module ConfCtl
|
|
28
116
|
end
|
29
117
|
end
|
30
118
|
|
119
|
+
def auto_rollback?
|
120
|
+
meta.fetch('autoRollback', {}).fetch('enable', true)
|
121
|
+
end
|
122
|
+
|
31
123
|
def health_checks
|
32
124
|
return @health_checks if @health_checks
|
33
125
|
|
34
126
|
@health_checks = []
|
35
127
|
|
36
|
-
|
128
|
+
meta['healthChecks'].each do |type, checks|
|
37
129
|
case type
|
38
130
|
when 'systemd'
|
39
131
|
next if !checks['enable'] || spin != 'nixos'
|
@@ -76,11 +168,13 @@ module ConfCtl
|
|
76
168
|
|
77
169
|
def [](key)
|
78
170
|
if key.index('.')
|
79
|
-
get(
|
171
|
+
get(meta, key.split('.'))
|
172
|
+
elsif key == 'name'
|
173
|
+
name
|
80
174
|
elsif key == 'checks'
|
81
175
|
health_checks.length
|
82
176
|
else
|
83
|
-
|
177
|
+
meta[key]
|
84
178
|
end
|
85
179
|
end
|
86
180
|
|
@@ -6,10 +6,11 @@ module ConfCtl
|
|
6
6
|
attr_reader :machine
|
7
7
|
|
8
8
|
# @param machine [Machine]
|
9
|
-
|
9
|
+
# @param logger [#<<]
|
10
|
+
def initialize(machine, logger: nil)
|
10
11
|
@machine = machine
|
11
12
|
@extra_ssh_opts = []
|
12
|
-
@cmd = SystemCommand.new
|
13
|
+
@cmd = SystemCommand.new(logger:)
|
13
14
|
end
|
14
15
|
|
15
16
|
# Try to open SSH connection
|
@@ -108,6 +109,13 @@ module ConfCtl
|
|
108
109
|
out.strip
|
109
110
|
end
|
110
111
|
|
112
|
+
# @param path [String]
|
113
|
+
# @return [String]
|
114
|
+
def read_realpath(path)
|
115
|
+
out, = run_cmd('realpath', path)
|
116
|
+
out.strip
|
117
|
+
end
|
118
|
+
|
111
119
|
# Execute command, raises exception on error
|
112
120
|
# @yieldparam out [String]
|
113
121
|
# @yieldparam err [String]
|
data/lib/confctl/machine_list.rb
CHANGED
@@ -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]
|
@@ -79,11 +90,17 @@ module ConfCtl
|
|
79
90
|
!empty?
|
80
91
|
end
|
81
92
|
|
93
|
+
def to_a
|
94
|
+
@machines.values
|
95
|
+
end
|
96
|
+
|
82
97
|
# @return [Array<HealthChecks::Base>]
|
83
98
|
def health_checks
|
84
99
|
checks = []
|
85
100
|
|
86
101
|
machines.each_value do |machine|
|
102
|
+
next if machine.carried?
|
103
|
+
|
87
104
|
checks.concat(machine.health_checks)
|
88
105
|
end
|
89
106
|
|
@@ -101,7 +118,7 @@ module ConfCtl
|
|
101
118
|
|
102
119
|
def parse(data)
|
103
120
|
data.transform_values do |info|
|
104
|
-
Machine.new(info)
|
121
|
+
Machine.new(info, machine_list: self)
|
105
122
|
end
|
106
123
|
end
|
107
124
|
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 =
|
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(machine, 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 =
|
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
|