confctl 1.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.
- checksums.yaml +7 -0
- data/.editorconfig +11 -0
- data/.gitignore +8 -0
- data/.overcommit.yml +6 -0
- data/.rubocop.yml +67 -0
- data/.rubocop_todo.yml +5 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +674 -0
- data/README.md +522 -0
- data/Rakefile +40 -0
- data/bin/confctl +4 -0
- data/confctl.gemspec +33 -0
- data/example/.gitignore +2 -0
- data/example/README.md +38 -0
- data/example/cluster/cluster.nix +7 -0
- data/example/cluster/module-list.nix +3 -0
- data/example/cluster/nixos-machine/config.nix +15 -0
- data/example/cluster/nixos-machine/hardware.nix +4 -0
- data/example/cluster/nixos-machine/module.nix +8 -0
- data/example/cluster/vpsadminos-container/config.nix +22 -0
- data/example/cluster/vpsadminos-container/module.nix +8 -0
- data/example/cluster/vpsadminos-machine/config.nix +22 -0
- data/example/cluster/vpsadminos-machine/hardware.nix +4 -0
- data/example/cluster/vpsadminos-machine/module.nix +8 -0
- data/example/cluster/vpsfreecz-vps/config.nix +25 -0
- data/example/cluster/vpsfreecz-vps/module.nix +8 -0
- data/example/configs/confctl.nix +10 -0
- data/example/configs/swpins.nix +28 -0
- data/example/data/default.nix +5 -0
- data/example/data/ssh-keys.nix +7 -0
- data/example/environments/base.nix +13 -0
- data/example/modules/module-list.nix +13 -0
- data/example/shell.nix +11 -0
- data/example/swpins/channels/nixos-unstable.json +35 -0
- data/example/swpins/channels/vpsadminos-staging.json +35 -0
- data/lib/confctl/cli/app.rb +551 -0
- data/lib/confctl/cli/attr_filters.rb +51 -0
- data/lib/confctl/cli/cluster.rb +1248 -0
- data/lib/confctl/cli/command.rb +206 -0
- data/lib/confctl/cli/configuration.rb +296 -0
- data/lib/confctl/cli/gen_data.rb +97 -0
- data/lib/confctl/cli/generation.rb +335 -0
- data/lib/confctl/cli/log_view.rb +267 -0
- data/lib/confctl/cli/output_formatter.rb +288 -0
- data/lib/confctl/cli/swpins/base.rb +40 -0
- data/lib/confctl/cli/swpins/channel.rb +73 -0
- data/lib/confctl/cli/swpins/cluster.rb +80 -0
- data/lib/confctl/cli/swpins/core.rb +86 -0
- data/lib/confctl/cli/swpins/utils.rb +55 -0
- data/lib/confctl/cli/swpins.rb +5 -0
- data/lib/confctl/cli/tag_filters.rb +30 -0
- data/lib/confctl/cli.rb +5 -0
- data/lib/confctl/conf_cache.rb +105 -0
- data/lib/confctl/conf_dir.rb +88 -0
- data/lib/confctl/erb_template.rb +37 -0
- data/lib/confctl/exceptions.rb +3 -0
- data/lib/confctl/gcroot.rb +30 -0
- data/lib/confctl/generation/build.rb +145 -0
- data/lib/confctl/generation/build_list.rb +106 -0
- data/lib/confctl/generation/host.rb +35 -0
- data/lib/confctl/generation/host_list.rb +81 -0
- data/lib/confctl/generation/unified.rb +117 -0
- data/lib/confctl/generation/unified_list.rb +63 -0
- data/lib/confctl/git_repo_mirror.rb +79 -0
- data/lib/confctl/health_checks/base.rb +66 -0
- data/lib/confctl/health_checks/run_command.rb +179 -0
- data/lib/confctl/health_checks/systemd/properties.rb +84 -0
- data/lib/confctl/health_checks/systemd/property_check.rb +31 -0
- data/lib/confctl/health_checks/systemd/property_list.rb +20 -0
- data/lib/confctl/health_checks.rb +5 -0
- data/lib/confctl/hook.rb +35 -0
- data/lib/confctl/line_buffer.rb +53 -0
- data/lib/confctl/logger.rb +151 -0
- data/lib/confctl/machine.rb +107 -0
- data/lib/confctl/machine_control.rb +172 -0
- data/lib/confctl/machine_list.rb +108 -0
- data/lib/confctl/machine_status.rb +135 -0
- data/lib/confctl/module_options.rb +95 -0
- data/lib/confctl/nix.rb +382 -0
- data/lib/confctl/nix_build.rb +108 -0
- data/lib/confctl/nix_collect_garbage.rb +64 -0
- data/lib/confctl/nix_copy.rb +49 -0
- data/lib/confctl/nix_format.rb +124 -0
- data/lib/confctl/nix_literal_expression.rb +15 -0
- data/lib/confctl/parallel_executor.rb +43 -0
- data/lib/confctl/pattern.rb +9 -0
- data/lib/confctl/settings.rb +50 -0
- data/lib/confctl/std_line_buffer.rb +40 -0
- data/lib/confctl/swpins/change_set.rb +151 -0
- data/lib/confctl/swpins/channel.rb +62 -0
- data/lib/confctl/swpins/channel_list.rb +47 -0
- data/lib/confctl/swpins/cluster_name.rb +94 -0
- data/lib/confctl/swpins/cluster_name_list.rb +15 -0
- data/lib/confctl/swpins/core.rb +137 -0
- data/lib/confctl/swpins/deployed_info.rb +23 -0
- data/lib/confctl/swpins/spec.rb +20 -0
- data/lib/confctl/swpins/specs/base.rb +184 -0
- data/lib/confctl/swpins/specs/directory.rb +51 -0
- data/lib/confctl/swpins/specs/git.rb +135 -0
- data/lib/confctl/swpins/specs/git_rev.rb +24 -0
- data/lib/confctl/swpins.rb +17 -0
- data/lib/confctl/system_command.rb +10 -0
- data/lib/confctl/user_script.rb +13 -0
- data/lib/confctl/user_scripts.rb +41 -0
- data/lib/confctl/utils/file.rb +21 -0
- data/lib/confctl/version.rb +3 -0
- data/lib/confctl.rb +43 -0
- data/man/man8/confctl-options.nix.8 +1334 -0
- data/man/man8/confctl-options.nix.8.md +1340 -0
- data/man/man8/confctl.8 +660 -0
- data/man/man8/confctl.8.md +654 -0
- data/nix/evaluator.nix +160 -0
- data/nix/lib/default.nix +83 -0
- data/nix/lib/machine/default.nix +74 -0
- data/nix/lib/machine/info.nix +5 -0
- data/nix/lib/swpins/eval.nix +71 -0
- data/nix/lib/swpins/options.nix +94 -0
- data/nix/machines.nix +31 -0
- data/nix/modules/cluster/default.nix +459 -0
- data/nix/modules/confctl/cli.nix +21 -0
- data/nix/modules/confctl/generations.nix +84 -0
- data/nix/modules/confctl/nix.nix +28 -0
- data/nix/modules/confctl/swpins.nix +55 -0
- data/nix/modules/module-list.nix +19 -0
- data/shell.nix +42 -0
- data/template/confctl-options.nix/main.erb +45 -0
- data/template/confctl-options.nix/options.erb +15 -0
- metadata +353 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module ConfCtl
|
|
4
|
+
# Format Ruby values in Nix
|
|
5
|
+
class NixFormat
|
|
6
|
+
# @param value [any]
|
|
7
|
+
# @param sort [Boolean] sort hash keys
|
|
8
|
+
# @return [String]
|
|
9
|
+
def self.to_nix(value, sort: true)
|
|
10
|
+
new(sort:).to_nix(value)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @param sort [Boolean] sort hash keys
|
|
14
|
+
def initialize(sort: true)
|
|
15
|
+
@sort = sort
|
|
16
|
+
@output = ''
|
|
17
|
+
@level = 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param value [any]
|
|
21
|
+
# @return [String]
|
|
22
|
+
def to_nix(value)
|
|
23
|
+
format_value(value).strip
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def pad(str, indent: true)
|
|
27
|
+
if indent
|
|
28
|
+
"#{' ' * @level}#{str}"
|
|
29
|
+
else
|
|
30
|
+
str
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def indent
|
|
35
|
+
@level += 2
|
|
36
|
+
yield
|
|
37
|
+
@level -= 2
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
protected
|
|
41
|
+
|
|
42
|
+
def format_value(value, indent: true, semicolon: false, nl: false)
|
|
43
|
+
if value.respond_to?(:to_nix)
|
|
44
|
+
return value.to_nix(nix_format: self, indent:, semicolon:, nl:)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
case value
|
|
48
|
+
when Hash
|
|
49
|
+
format_hash(value, indent:, semicolon:, nl:)
|
|
50
|
+
|
|
51
|
+
when Array
|
|
52
|
+
format_array(value, indent:, semicolon:, nl:)
|
|
53
|
+
|
|
54
|
+
else
|
|
55
|
+
simple =
|
|
56
|
+
case value
|
|
57
|
+
when String
|
|
58
|
+
value.dump
|
|
59
|
+
|
|
60
|
+
when Symbol
|
|
61
|
+
value.to_s.dump
|
|
62
|
+
|
|
63
|
+
when Numeric, true, false
|
|
64
|
+
value.to_s
|
|
65
|
+
|
|
66
|
+
when Pathname
|
|
67
|
+
str = value.to_s
|
|
68
|
+
|
|
69
|
+
if str.start_with?('/')
|
|
70
|
+
str
|
|
71
|
+
else
|
|
72
|
+
"./#{str}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
when nil
|
|
76
|
+
'null'
|
|
77
|
+
|
|
78
|
+
else
|
|
79
|
+
raise "Unable to format #{value.inspect} (#{value.class}) to nix: " \
|
|
80
|
+
'implement #to_nix()?'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
pad("#{simple}#{semicolon ? ';' : ''}#{nl ? "\n" : ''}", indent:)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def format_hash(hash, indent: true, semicolon: false, nl: true)
|
|
88
|
+
ret = pad("{\n", indent:)
|
|
89
|
+
|
|
90
|
+
quote_keys = !hash.each_key.all? { |k| /^[a-zA-Z_][a-zA-Z_\-']*$/ =~ k }
|
|
91
|
+
|
|
92
|
+
sorted =
|
|
93
|
+
if @sort
|
|
94
|
+
hash.sort do |a, b|
|
|
95
|
+
a[0] <=> b[0]
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
hash
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
indent do
|
|
102
|
+
sorted.each do |k, v|
|
|
103
|
+
ret << (quote_keys ? pad("\"#{k}\"") : pad(k.to_s))
|
|
104
|
+
ret << " = #{format_value(v, indent: false, semicolon: true, nl: false)}\n"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
ret << pad("}#{semicolon ? ';' : ''}#{nl ? "\n" : ''}")
|
|
109
|
+
ret
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def format_array(array, indent: true, semicolon: false, nl: true)
|
|
113
|
+
ret = pad("[\n", indent:)
|
|
114
|
+
|
|
115
|
+
indent do
|
|
116
|
+
array.each do |v|
|
|
117
|
+
ret << "#{pad(format_value(v, indent: false, nl: false))}\n"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
ret << pad("]#{semicolon ? ';' : ''}#{nl ? "\n" : ''}")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module ConfCtl
|
|
2
|
+
class ParallelExecutor
|
|
3
|
+
attr_reader :thread_count
|
|
4
|
+
|
|
5
|
+
def initialize(threads)
|
|
6
|
+
@thread_count = threads
|
|
7
|
+
@threads = []
|
|
8
|
+
@queue = Queue.new
|
|
9
|
+
@retvals = []
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add(&block)
|
|
14
|
+
queue << block
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
thread_count.times do
|
|
19
|
+
threads << Thread.new { worker }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
threads.each(&:join)
|
|
23
|
+
retvals
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
attr_reader :threads, :queue, :mutex, :retvals
|
|
29
|
+
|
|
30
|
+
def worker
|
|
31
|
+
loop do
|
|
32
|
+
begin
|
|
33
|
+
block = queue.pop(true)
|
|
34
|
+
rescue ThreadError
|
|
35
|
+
return
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
v = block.call
|
|
39
|
+
mutex.synchronize { retvals << v }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
module ConfCtl
|
|
4
|
+
class Settings
|
|
5
|
+
include Singleton
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@settings = nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def list_columns
|
|
12
|
+
read_settings { |s| s['list']['columns'] }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def max_jobs
|
|
16
|
+
read_settings { |s| s['nix']['maxJobs'] }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def nix_paths
|
|
20
|
+
read_settings { |s| s['nix']['nixPath'] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def core_swpin_channels
|
|
24
|
+
read_settings { |s| s['swpins']['core']['channels'] }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def core_swpin_pins
|
|
28
|
+
read_settings { |s| s['swpins']['core']['pins'] }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def build_generations
|
|
32
|
+
read_settings { |s| s['buildGenerations'] }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def host_generations
|
|
36
|
+
read_settings { |s| s['hostGenerations'] }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
|
|
41
|
+
def read_settings
|
|
42
|
+
if @settings.nil?
|
|
43
|
+
nix = Nix.stateless
|
|
44
|
+
@settings = nix.confctl_settings
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
yield(@settings)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module ConfCtl
|
|
2
|
+
# Pair of line buffers for standard output/error
|
|
3
|
+
class StdLineBuffer
|
|
4
|
+
# @yieldparam out [String, nil]
|
|
5
|
+
# @yieldparam err [String, nil]
|
|
6
|
+
def initialize(&block)
|
|
7
|
+
@out_buffer = LineBuffer.new
|
|
8
|
+
@err_buffer = LineBuffer.new
|
|
9
|
+
@block = block
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Get a block which can be called to feed data to the buffer
|
|
14
|
+
# @return [Proc]
|
|
15
|
+
def feed_block
|
|
16
|
+
proc do |stdout, stderr|
|
|
17
|
+
@mutex.synchronize do
|
|
18
|
+
out_buffer << stdout if stdout
|
|
19
|
+
err_buffer << stderr if stderr
|
|
20
|
+
|
|
21
|
+
loop do
|
|
22
|
+
out_line = out_buffer.read_line
|
|
23
|
+
err_line = err_buffer.read_line
|
|
24
|
+
break if out_line.nil? && err_line.nil?
|
|
25
|
+
|
|
26
|
+
block.call(out_line, err_line)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def flush
|
|
33
|
+
block.call(out_buffer.flush, err_buffer.flush)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
|
|
38
|
+
attr_reader :out_buffer, :err_buffer, :block
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
module ConfCtl
|
|
2
|
+
class Swpins::ChangeSet
|
|
3
|
+
class SpecOwner
|
|
4
|
+
attr_reader :name, :path
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
SpecSet = Struct.new(:spec, :original_version, :original_info, keyword_init: true) do
|
|
8
|
+
def name
|
|
9
|
+
spec.name
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def new_version
|
|
13
|
+
spec.version
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def new_info
|
|
17
|
+
spec.info
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def changed?
|
|
21
|
+
original_version != new_version
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def string_changelog(type)
|
|
25
|
+
if original_info
|
|
26
|
+
spec.string_changelog_info(type, original_info, color: false)
|
|
27
|
+
else
|
|
28
|
+
"Initial version\n"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def initialize
|
|
34
|
+
@owners = {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param owner [SpecOwner]
|
|
38
|
+
# @param spec [Swpins::Spec]
|
|
39
|
+
def add(owner, spec)
|
|
40
|
+
@owners[owner] ||= []
|
|
41
|
+
@owners[owner] << SpecSet.new(
|
|
42
|
+
spec:,
|
|
43
|
+
original_version: spec.valid? ? spec.version : nil,
|
|
44
|
+
original_info: spec.valid? ? spec.info : nil
|
|
45
|
+
)
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param type [:upgrade, :downgrade]
|
|
50
|
+
# @param changelog [Boolean]
|
|
51
|
+
def commit(type: :upgrade, changelog: false)
|
|
52
|
+
return if @owners.empty?
|
|
53
|
+
|
|
54
|
+
ret = Kernel.system(
|
|
55
|
+
'git',
|
|
56
|
+
'commit',
|
|
57
|
+
'-e',
|
|
58
|
+
'-m', build_message(type, changelog),
|
|
59
|
+
*changed_files
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return if ret
|
|
63
|
+
|
|
64
|
+
raise 'git commit exited with non-zero status code'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
protected
|
|
68
|
+
|
|
69
|
+
def build_message(type, changelog)
|
|
70
|
+
[
|
|
71
|
+
build_title(type, changelog),
|
|
72
|
+
build_description(type, changelog)
|
|
73
|
+
].join("\n\n")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def build_title(type, changelog)
|
|
77
|
+
msg = 'swpins: '
|
|
78
|
+
|
|
79
|
+
if same_changes?
|
|
80
|
+
spec_sets = @owners.first[1]
|
|
81
|
+
msg << "#{@owners.each_key.map(&:name).sort.join(', ')}: update "
|
|
82
|
+
|
|
83
|
+
msg << spec_sets.map do |spec_set|
|
|
84
|
+
"#{spec_set.name} to #{spec_set.new_version}"
|
|
85
|
+
end.join(', ')
|
|
86
|
+
else
|
|
87
|
+
all_spec_names = []
|
|
88
|
+
|
|
89
|
+
@owners.each_value do |spec_sets|
|
|
90
|
+
spec_sets.each do |spec_set|
|
|
91
|
+
all_spec_names << spec_set.name unless all_spec_names.include?(spec_set.name)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
msg << "#{@owners.each_key.map(&:name).join(', ')}: update #{all_spec_names.sort.join(', ')}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
msg
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def build_description(type, changelog)
|
|
102
|
+
msg = ''
|
|
103
|
+
|
|
104
|
+
@owners.each do |owner, spec_sets|
|
|
105
|
+
spec_sets.each do |spec_set|
|
|
106
|
+
if spec_set.changed?
|
|
107
|
+
msg << "#{owner.name} #{spec_set.name}: "
|
|
108
|
+
|
|
109
|
+
msg << if spec_set.original_version
|
|
110
|
+
"#{spec_set.original_version} -> #{spec_set.new_version}\n"
|
|
111
|
+
else
|
|
112
|
+
"set to #{spec_set.new_version}\n"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if changelog
|
|
116
|
+
msg << spec_set.string_changelog(type)
|
|
117
|
+
msg << "\n"
|
|
118
|
+
end
|
|
119
|
+
else
|
|
120
|
+
msg << " #{spec_set.name}: unchanged\n"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
msg << "\n" if changelog
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
msg
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def changed_files
|
|
131
|
+
@owners.each_key.map(&:path)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def same_changes?
|
|
135
|
+
return true if @owners.length == 1
|
|
136
|
+
|
|
137
|
+
expected_spec_sets = @owners.first[1]
|
|
138
|
+
|
|
139
|
+
@owners.each_value do |spec_sets|
|
|
140
|
+
return false if expected_spec_sets.length != spec_sets.length
|
|
141
|
+
|
|
142
|
+
spec_sets.each do |spec_set|
|
|
143
|
+
expected = expected_spec_sets.detect { |v| v.name == spec_set.name }
|
|
144
|
+
return false if expected.nil? || expected.new_version != spec_set.new_version
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
true
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module ConfCtl
|
|
5
|
+
class Swpins::Channel
|
|
6
|
+
# @return [String]
|
|
7
|
+
attr_reader :path
|
|
8
|
+
|
|
9
|
+
# @return [String]
|
|
10
|
+
attr_reader :name
|
|
11
|
+
|
|
12
|
+
# @return [Hash<String, Swpins::Specs::Base>]
|
|
13
|
+
attr_reader :specs
|
|
14
|
+
|
|
15
|
+
# @param name [String]
|
|
16
|
+
# @param nix_specs [Hash]
|
|
17
|
+
def initialize(name, nix_specs)
|
|
18
|
+
@name = name
|
|
19
|
+
@path = File.join(ConfCtl::Swpins.channel_dir, "#{name}.json")
|
|
20
|
+
@nix_specs = nix_specs
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parse
|
|
24
|
+
@json_specs = if File.exist?(path)
|
|
25
|
+
JSON.parse(File.read(path))
|
|
26
|
+
else
|
|
27
|
+
{}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@specs = nix_specs.to_h do |name, nix_opts|
|
|
31
|
+
[
|
|
32
|
+
name,
|
|
33
|
+
Swpins::Spec.for(nix_opts['type'].to_sym).new(
|
|
34
|
+
name,
|
|
35
|
+
nix_opts[nix_opts['type']],
|
|
36
|
+
json_specs[name]
|
|
37
|
+
)
|
|
38
|
+
]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def valid?
|
|
43
|
+
specs.values.all?(&:valid?)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def save
|
|
47
|
+
tmp = "#{path}.new"
|
|
48
|
+
|
|
49
|
+
FileUtils.mkdir_p(ConfCtl::Swpins.channel_dir)
|
|
50
|
+
|
|
51
|
+
File.open(tmp, 'w') do |f|
|
|
52
|
+
f.puts(JSON.pretty_generate(specs))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
File.rename(tmp, path)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
protected
|
|
59
|
+
|
|
60
|
+
attr_reader :nix_specs, :json_specs
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module ConfCtl
|
|
2
|
+
class Swpins::ChannelList < Array
|
|
3
|
+
# @return [Swpins::ChannelList]
|
|
4
|
+
def self.get
|
|
5
|
+
@get ||= new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# @param pattern [String]
|
|
9
|
+
def self.pattern(pattern)
|
|
10
|
+
get.pattern(pattern)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.refresh
|
|
14
|
+
get.refresh
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param pattern [String]
|
|
18
|
+
def initialize(pattern: '*')
|
|
19
|
+
super()
|
|
20
|
+
parse(pattern:)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param pattern [String]
|
|
24
|
+
# @return [Array<Swpins::Channel>]
|
|
25
|
+
def pattern(pattern)
|
|
26
|
+
select { |c| Pattern.match?(pattern, c.name) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def refresh
|
|
30
|
+
clear
|
|
31
|
+
parse
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
|
|
36
|
+
def parse(pattern: '*')
|
|
37
|
+
nix = Nix.new
|
|
38
|
+
nix.list_swpins_channels.each do |name, nix_specs|
|
|
39
|
+
next unless Pattern.match?(pattern, name)
|
|
40
|
+
|
|
41
|
+
c = Swpins::Channel.new(name, nix_specs)
|
|
42
|
+
c.parse
|
|
43
|
+
self << c
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module ConfCtl
|
|
5
|
+
class Swpins::ClusterName
|
|
6
|
+
# @return [Machine]
|
|
7
|
+
attr_reader :machine
|
|
8
|
+
|
|
9
|
+
# @return [String]
|
|
10
|
+
attr_reader :name
|
|
11
|
+
|
|
12
|
+
# @return [String]
|
|
13
|
+
attr_reader :path
|
|
14
|
+
|
|
15
|
+
# @return [Hash<String, Swpins::Specs::Base>]
|
|
16
|
+
attr_reader :specs
|
|
17
|
+
|
|
18
|
+
# @return [Array<Swpins::Channel>]
|
|
19
|
+
attr_reader :channels
|
|
20
|
+
|
|
21
|
+
# @param machine [Machine]
|
|
22
|
+
# @param channels [Swpins::ChannelList]
|
|
23
|
+
def initialize(machine, channels)
|
|
24
|
+
@machine = machine
|
|
25
|
+
@name = machine.name
|
|
26
|
+
@path = File.join(ConfCtl::Swpins.cluster_dir, "#{name.gsub('/', ':')}.json")
|
|
27
|
+
@channels = channels.select do |c|
|
|
28
|
+
machine['swpins']['channels'].include?(c.name)
|
|
29
|
+
end
|
|
30
|
+
@nix_specs = machine['swpins']['pins']
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse
|
|
34
|
+
@specs = {}
|
|
35
|
+
|
|
36
|
+
# Add specs from channels
|
|
37
|
+
channels.each do |chan|
|
|
38
|
+
chan.specs.each do |name, chan_spec|
|
|
39
|
+
s = chan_spec.clone
|
|
40
|
+
s.channel = chan.name
|
|
41
|
+
specs[name] = s
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Add machine-specific specs
|
|
46
|
+
@json_specs = if File.exist?(path)
|
|
47
|
+
JSON.parse(File.read(path))
|
|
48
|
+
else
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
nix_specs.each do |name, nix_opts|
|
|
53
|
+
specs[name] = Swpins::Spec.for(nix_opts['type'].to_sym).new(
|
|
54
|
+
name,
|
|
55
|
+
nix_opts[nix_opts['type']],
|
|
56
|
+
json_specs[name]
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def valid?
|
|
62
|
+
specs.values.all?(&:valid?)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def save
|
|
66
|
+
custom = {}
|
|
67
|
+
specs.each do |name, s|
|
|
68
|
+
custom[name] = s unless s.from_channel?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if custom.empty?
|
|
72
|
+
begin
|
|
73
|
+
File.unlink(path)
|
|
74
|
+
rescue Errno::ENOENT
|
|
75
|
+
# ignore
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
tmp = "#{path}.new"
|
|
79
|
+
|
|
80
|
+
FileUtils.mkdir_p(ConfCtl::Swpins.cluster_dir)
|
|
81
|
+
|
|
82
|
+
File.open(tmp, 'w') do |f|
|
|
83
|
+
f.puts(JSON.pretty_generate(custom))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
File.rename(tmp, path)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
protected
|
|
91
|
+
|
|
92
|
+
attr_reader :nix_specs, :json_specs
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module ConfCtl
|
|
2
|
+
class Swpins::ClusterNameList < Array
|
|
3
|
+
# @param channels [Swpins::ChannelList]
|
|
4
|
+
# @param pattern [String]
|
|
5
|
+
# @param machines [MachineList]
|
|
6
|
+
def initialize(channels: nil, pattern: '*', machines: nil)
|
|
7
|
+
super()
|
|
8
|
+
channels ||= ConfCtl::Swpins::ChannelList.get
|
|
9
|
+
|
|
10
|
+
(machines || MachineList.new).each_value do |machine|
|
|
11
|
+
self << Swpins::ClusterName.new(machine, channels) if Pattern.match?(pattern, machine.name)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|