confctl 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,206 @@
|
|
1
|
+
require 'gli'
|
2
|
+
require 'rainbow'
|
3
|
+
|
4
|
+
module ConfCtl::Cli
|
5
|
+
class Command
|
6
|
+
def self.run(gli_cmd, klass, method)
|
7
|
+
proc do |global_opts, opts, args|
|
8
|
+
log = ConfCtl::Logger.instance
|
9
|
+
log.open(gli_cmd.name_for_help.join('-'))
|
10
|
+
log.cli(
|
11
|
+
gli_cmd.name_for_help,
|
12
|
+
global_opts,
|
13
|
+
opts,
|
14
|
+
args
|
15
|
+
)
|
16
|
+
|
17
|
+
cmd = klass.new(global_opts, opts, args)
|
18
|
+
cmd.run_method(method)
|
19
|
+
|
20
|
+
if log.keep?
|
21
|
+
log.close
|
22
|
+
else
|
23
|
+
log.close_and_unlink
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :gopts, :opts, :args
|
29
|
+
|
30
|
+
def initialize(global_opts, opts, args)
|
31
|
+
@gopts = global_opts
|
32
|
+
@opts = opts
|
33
|
+
@args = args
|
34
|
+
@use_color = determine_color
|
35
|
+
@use_pager = determine_pager
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param v [Array] list of required arguments
|
39
|
+
# @param optional [Array] list of optional arguments
|
40
|
+
# @param strict [Boolean] do not allow more arguments than specified
|
41
|
+
def require_args!(*required, optional: [], strict: true)
|
42
|
+
if args.count < required.count
|
43
|
+
arg = required[args.count]
|
44
|
+
raise GLI::BadCommandLine, "missing argument <#{arg}>"
|
45
|
+
|
46
|
+
elsif strict && args.count > (required.count + optional.count)
|
47
|
+
unknown = args[(required.count + optional.count)..]
|
48
|
+
|
49
|
+
msg = ''
|
50
|
+
|
51
|
+
msg << if unknown.count > 1
|
52
|
+
'unknown arguments: '
|
53
|
+
else
|
54
|
+
'unknown argument: '
|
55
|
+
end
|
56
|
+
|
57
|
+
msg << unknown.join(' ')
|
58
|
+
|
59
|
+
msg << ' (note that options must come before arguments)' if unknown.detect { |v| v.start_with?('-') }
|
60
|
+
|
61
|
+
raise GLI::BadCommandLine, msg
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def use_color?
|
66
|
+
@use_color
|
67
|
+
end
|
68
|
+
|
69
|
+
def use_pager?
|
70
|
+
@use_pager
|
71
|
+
end
|
72
|
+
|
73
|
+
def ask_confirmation(always: false)
|
74
|
+
return true if !always && opts[:yes]
|
75
|
+
|
76
|
+
yield if block_given?
|
77
|
+
|
78
|
+
loop do
|
79
|
+
$stdout.write("\nContinue? [y/N]: ")
|
80
|
+
$stdout.flush
|
81
|
+
|
82
|
+
case $stdin.readline.strip.downcase
|
83
|
+
when 'y'
|
84
|
+
puts
|
85
|
+
return true
|
86
|
+
when 'n'
|
87
|
+
puts
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def ask_confirmation!(...)
|
94
|
+
raise 'Aborted' unless ask_confirmation(...)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param options [Hash<String, String>] key => description
|
98
|
+
# @param default [String] default option key
|
99
|
+
# @return [String] selection option key
|
100
|
+
def ask_action(options:, default:)
|
101
|
+
yield if block_given?
|
102
|
+
|
103
|
+
loop do
|
104
|
+
$stdout.puts("\nOptions:\n")
|
105
|
+
|
106
|
+
options.each do |key, desc|
|
107
|
+
$stdout.puts(" [#{key}] #{desc}")
|
108
|
+
end
|
109
|
+
|
110
|
+
keys = options.keys.map do |k|
|
111
|
+
if k == default
|
112
|
+
k.upcase
|
113
|
+
else
|
114
|
+
k
|
115
|
+
end
|
116
|
+
end.join('/')
|
117
|
+
|
118
|
+
$stdout.write("\nAction: [#{keys}]: ")
|
119
|
+
$stdout.flush
|
120
|
+
|
121
|
+
answer = $stdin.readline.strip.downcase
|
122
|
+
|
123
|
+
if options.has_key?(answer)
|
124
|
+
puts
|
125
|
+
return answer
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def run_method(method)
|
131
|
+
self.method(method).call
|
132
|
+
end
|
133
|
+
|
134
|
+
protected
|
135
|
+
|
136
|
+
def run_command(klass, method)
|
137
|
+
c = klass.new(gopts, opts, args)
|
138
|
+
c.run_method(method)
|
139
|
+
end
|
140
|
+
|
141
|
+
def determine_color
|
142
|
+
case gopts[:color]
|
143
|
+
when 'always'
|
144
|
+
Rainbow.enabled = true
|
145
|
+
true
|
146
|
+
when 'never'
|
147
|
+
Rainbow.enabled = false
|
148
|
+
false
|
149
|
+
when 'auto'
|
150
|
+
Rainbow.enabled
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def determine_pager
|
155
|
+
ENV.fetch('PAGER', nil) && ENV['PAGER'].strip != ''
|
156
|
+
end
|
157
|
+
|
158
|
+
def select_machines(pattern)
|
159
|
+
machines = ConfCtl::MachineList.new(show_trace: opts['show-trace'])
|
160
|
+
|
161
|
+
attr_filters = AttrFilters.new(opts[:attr])
|
162
|
+
tag_filters = TagFilters.new(opts[:tag])
|
163
|
+
|
164
|
+
machines.select do |host, d|
|
165
|
+
(pattern.nil? || ConfCtl::Pattern.match?(pattern, host)) \
|
166
|
+
&& attr_filters.pass?(d) \
|
167
|
+
&& tag_filters.pass?(d)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def select_machines_with_managed(pattern)
|
172
|
+
selected = select_machines(pattern)
|
173
|
+
|
174
|
+
case opts[:managed]
|
175
|
+
when 'n', 'no'
|
176
|
+
selected.unmanaged
|
177
|
+
when 'a', 'all'
|
178
|
+
selected
|
179
|
+
else
|
180
|
+
selected.managed
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def list_machines(machines, prepend_cols: [])
|
185
|
+
cols =
|
186
|
+
if opts[:output]
|
187
|
+
opts[:output].split(',')
|
188
|
+
else
|
189
|
+
ConfCtl::Settings.instance.list_columns
|
190
|
+
end
|
191
|
+
|
192
|
+
cols = prepend_cols + cols if prepend_cols
|
193
|
+
|
194
|
+
rows = machines.map do |_host, machine|
|
195
|
+
cols.to_h { |c| [c, machine[c]] }
|
196
|
+
end
|
197
|
+
|
198
|
+
OutputFormatter.print(
|
199
|
+
rows,
|
200
|
+
cols,
|
201
|
+
header: !opts['hide-header'],
|
202
|
+
layout: :columns
|
203
|
+
)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module ConfCtl::Cli
|
5
|
+
class Configuration < Command
|
6
|
+
DIR_MODE = 0o755
|
7
|
+
FILE_MODE = 0o644
|
8
|
+
|
9
|
+
def init
|
10
|
+
dir = File.realpath(Dir.pwd)
|
11
|
+
|
12
|
+
Dir.entries(dir).each do |v|
|
13
|
+
raise 'init must be called in an empty directory' unless %w[. .. shell.nix .confctl .gems
|
14
|
+
.gitignore].include?(v)
|
15
|
+
end
|
16
|
+
|
17
|
+
mkdir('cluster')
|
18
|
+
|
19
|
+
mkfile('cluster/module-list.nix') do |f|
|
20
|
+
f.puts(<<~END
|
21
|
+
(import ./cluster.nix) ++ [
|
22
|
+
# Place for custom modules
|
23
|
+
]
|
24
|
+
END
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
mkfile('cluster/cluster.nix') do |f|
|
29
|
+
f.puts(<<~END
|
30
|
+
# This file is generated by confctl, changes will be lost
|
31
|
+
[]
|
32
|
+
END
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
mkdir('configs')
|
37
|
+
mkfile('configs/confctl.nix') do |f|
|
38
|
+
f.puts(<<~END
|
39
|
+
{ config, ... }:
|
40
|
+
{
|
41
|
+
confctl = {
|
42
|
+
# listColumns = {
|
43
|
+
# "name"
|
44
|
+
# "spin"
|
45
|
+
# "host.fqdn"
|
46
|
+
# };
|
47
|
+
};
|
48
|
+
}
|
49
|
+
END
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
mkfile('configs/swpins.nix') do |f|
|
54
|
+
f.puts(<<~END
|
55
|
+
{ config, ... }:
|
56
|
+
let
|
57
|
+
nixpkgsBranch = branch: {
|
58
|
+
type = "git-rev";
|
59
|
+
|
60
|
+
git-rev = {
|
61
|
+
url = "https://github.com/NixOS/nixpkgs";
|
62
|
+
update.ref = "refs/heads/${branch}";
|
63
|
+
};
|
64
|
+
};
|
65
|
+
|
66
|
+
vpsadminosBranch = branch: {
|
67
|
+
type = "git-rev";
|
68
|
+
|
69
|
+
git-rev = {
|
70
|
+
url = "https://github.com/vpsfreecz/vpsadminos";
|
71
|
+
update.ref = "refs/heads/${branch}";
|
72
|
+
};
|
73
|
+
};
|
74
|
+
in {
|
75
|
+
confctl.swpins.channels = {
|
76
|
+
nixos-unstable = { nixpkgs = nixpkgsBranch "nixos-unstable"; };
|
77
|
+
|
78
|
+
# nixos-stable = { nixpkgs = nixpkgsBranch "nixos-20.09"; };
|
79
|
+
|
80
|
+
# vpsadminos-staging = { vpsadminos = vpsadminosBranch "staging"; };
|
81
|
+
};
|
82
|
+
}
|
83
|
+
END
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
mkdir('data')
|
88
|
+
|
89
|
+
mkfile('data/default.nix') do |f|
|
90
|
+
f.puts(<<~END
|
91
|
+
{ lib }:
|
92
|
+
{
|
93
|
+
# Place to load custom data sets
|
94
|
+
sshKeys = import ./ssh-keys.nix;
|
95
|
+
}
|
96
|
+
END
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
mkfile('data/ssh-keys.nix') do |f|
|
101
|
+
f.puts(<<~END
|
102
|
+
rec {
|
103
|
+
admins = [
|
104
|
+
# someone
|
105
|
+
];
|
106
|
+
|
107
|
+
someone = "...ssh public key...";
|
108
|
+
}
|
109
|
+
END
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
mkdir('environments')
|
114
|
+
|
115
|
+
mkfile('environments/base.nix') do |f|
|
116
|
+
f.puts(<<~END
|
117
|
+
{ config, pkgs, confData, ... }:
|
118
|
+
{
|
119
|
+
time.timeZone = "Europe/Amsterdam";
|
120
|
+
|
121
|
+
services.openssh.enable = true;
|
122
|
+
|
123
|
+
environment.systemPackages = with pkgs; [
|
124
|
+
vim
|
125
|
+
screen
|
126
|
+
];
|
127
|
+
|
128
|
+
users.users.root.openssh.authorizedKeys.keys = with confData.sshKeys; admins;
|
129
|
+
}
|
130
|
+
END
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
mkdir('modules')
|
135
|
+
mkfile('modules/module-list.nix') do |f|
|
136
|
+
f.puts(<<~END
|
137
|
+
rec {
|
138
|
+
shared = [
|
139
|
+
# Modules not dependent on spin
|
140
|
+
];
|
141
|
+
|
142
|
+
nixos = shared ++ [
|
143
|
+
# Modules only for NixOS
|
144
|
+
];
|
145
|
+
|
146
|
+
vpsadminos = shared ++ [
|
147
|
+
# Modules only for vpsAdminOS
|
148
|
+
];
|
149
|
+
}
|
150
|
+
END
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
mkdir('swpins')
|
155
|
+
mkdir('swpins/channels')
|
156
|
+
end
|
157
|
+
|
158
|
+
def add
|
159
|
+
require_args!('name')
|
160
|
+
|
161
|
+
name = args[0]
|
162
|
+
dir = File.join('cluster', name)
|
163
|
+
depth = name.count('/')
|
164
|
+
|
165
|
+
raise "#{dir} already exists" if Dir.exist?(dir)
|
166
|
+
|
167
|
+
mkdir_p(dir)
|
168
|
+
|
169
|
+
mkfile(File.join(dir, 'module.nix')) do |f|
|
170
|
+
f.puts(<<~END
|
171
|
+
{ config, ... }:
|
172
|
+
{
|
173
|
+
cluster."#{name}" = {
|
174
|
+
spin = "nixos";
|
175
|
+
swpins.channels = [ "nixos-unstable" ];
|
176
|
+
host = { name = "machine"; domain = "example.com"; };
|
177
|
+
};
|
178
|
+
}
|
179
|
+
END
|
180
|
+
)
|
181
|
+
end
|
182
|
+
|
183
|
+
mkfile(File.join(dir, 'config.nix')) do |f|
|
184
|
+
f.puts(<<~END
|
185
|
+
{ config, pkgs, lib, ... }:
|
186
|
+
{
|
187
|
+
imports = [
|
188
|
+
#{'../' * (depth + 2)}environments/base.nix
|
189
|
+
];
|
190
|
+
|
191
|
+
# ... standard NixOS configuration ...
|
192
|
+
|
193
|
+
networking.hostName = "#{name.gsub('/', '-')}";
|
194
|
+
}
|
195
|
+
END
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
rediscover
|
200
|
+
end
|
201
|
+
|
202
|
+
def rename
|
203
|
+
require_args!('old-name', 'new-name')
|
204
|
+
|
205
|
+
src = args[0]
|
206
|
+
dst = args[1]
|
207
|
+
|
208
|
+
src_path = File.join('cluster', src)
|
209
|
+
dst_path = File.join('cluster', dst)
|
210
|
+
|
211
|
+
if !Dir.exist?(src_path)
|
212
|
+
raise "'#{src}' not found"
|
213
|
+
elsif Dir.exist?(dst_path)
|
214
|
+
raise "'#{dst}' already exists"
|
215
|
+
end
|
216
|
+
|
217
|
+
dst_dir = File.dirname(dst_path)
|
218
|
+
mkdir_p(dst_dir)
|
219
|
+
mv(src_path, dst_path)
|
220
|
+
|
221
|
+
src_swpins = File.join('swpins/cluster', "#{src.gsub('/', ':')}.json")
|
222
|
+
dst_swpins = File.join('swpins/cluster', "#{dst.gsub('/', ':')}.json")
|
223
|
+
mv(src_swpins, dst_swpins) if File.exist?(src_swpins)
|
224
|
+
|
225
|
+
rediscover
|
226
|
+
end
|
227
|
+
|
228
|
+
def rediscover
|
229
|
+
hosts = discover_dir('cluster').sort
|
230
|
+
|
231
|
+
replace_file('cluster/cluster.nix') do |f|
|
232
|
+
f.puts('# This file is generated by confctl, changes will be lost')
|
233
|
+
f.puts('[')
|
234
|
+
|
235
|
+
hosts.each do |host|
|
236
|
+
f.puts(" ./#{host}/module.nix")
|
237
|
+
end
|
238
|
+
|
239
|
+
f.puts(']')
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
protected
|
244
|
+
|
245
|
+
def discover_dir(dir_path, rel_path = nil)
|
246
|
+
ret = []
|
247
|
+
|
248
|
+
Dir.entries(dir_path).each do |v|
|
249
|
+
entry_abs_path = File.join(dir_path, v)
|
250
|
+
next if %w[. ..].include?(v) || !File.directory?(entry_abs_path)
|
251
|
+
|
252
|
+
entry_rel_path = File.join(*[rel_path, v].compact)
|
253
|
+
|
254
|
+
if File.exist?(File.join(entry_abs_path, 'module.nix')) \
|
255
|
+
&& File.exist?(File.join(entry_abs_path, 'config.nix'))
|
256
|
+
ret << entry_rel_path
|
257
|
+
end
|
258
|
+
|
259
|
+
ret.concat(discover_dir(entry_abs_path, entry_rel_path))
|
260
|
+
end
|
261
|
+
|
262
|
+
ret
|
263
|
+
end
|
264
|
+
|
265
|
+
def mkdir(name)
|
266
|
+
puts "mkdir #{name}"
|
267
|
+
Dir.mkdir(name, DIR_MODE)
|
268
|
+
end
|
269
|
+
|
270
|
+
def mkdir_p(name)
|
271
|
+
puts "mkdir #{name}"
|
272
|
+
FileUtils.mkdir_p(name, mode: DIR_MODE)
|
273
|
+
end
|
274
|
+
|
275
|
+
def mkfile(name)
|
276
|
+
puts "mkfile #{name}"
|
277
|
+
f = File.open(name, 'w', FILE_MODE)
|
278
|
+
yield(f)
|
279
|
+
f.close
|
280
|
+
end
|
281
|
+
|
282
|
+
def replace_file(name, &)
|
283
|
+
puts "replace #{name}"
|
284
|
+
replacement = "#{name}.new-#{SecureRandom.hex(3)}"
|
285
|
+
|
286
|
+
File.open(replacement, 'w', FILE_MODE, &)
|
287
|
+
|
288
|
+
File.rename(replacement, name)
|
289
|
+
end
|
290
|
+
|
291
|
+
def mv(old_name, new_name)
|
292
|
+
puts "mv #{old_name} #{new_name}"
|
293
|
+
File.rename(old_name, new_name)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
require 'vpsfree/client'
|
3
|
+
require 'confctl/conf_dir'
|
4
|
+
|
5
|
+
module ConfCtl::Cli
|
6
|
+
class GenData < Command
|
7
|
+
def vpsadmin_all
|
8
|
+
vpsadmin_containers
|
9
|
+
vpsadmin_network
|
10
|
+
end
|
11
|
+
|
12
|
+
def vpsadmin_containers
|
13
|
+
api = vpsadmin_client
|
14
|
+
|
15
|
+
machines = ConfCtl::MachineList.new
|
16
|
+
data = {}
|
17
|
+
|
18
|
+
machines.each_value do |m|
|
19
|
+
next if m['container'].nil?
|
20
|
+
|
21
|
+
ct = api.vps.show(
|
22
|
+
m['container.id'],
|
23
|
+
meta: { includes: 'node__location__environment' }
|
24
|
+
)
|
25
|
+
|
26
|
+
ct_fqdn = [
|
27
|
+
m['host.name'],
|
28
|
+
m['host.location'],
|
29
|
+
m['host.domain']
|
30
|
+
].compact.join('.')
|
31
|
+
|
32
|
+
data[ct_fqdn] = {
|
33
|
+
node: {
|
34
|
+
id: ct.node.id,
|
35
|
+
name: ct.node.name,
|
36
|
+
location: ct.node.location.domain,
|
37
|
+
domain: ct.node.location.environment.domain,
|
38
|
+
fqdn: "#{ct.node.domain_name}.#{ct.node.location.environment.domain}"
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
update_file('vpsadmin/containers.nix') do |f|
|
44
|
+
f.puts(ConfCtl::NixFormat.to_nix(data))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def vpsadmin_network
|
49
|
+
vpsadmin_network_containers
|
50
|
+
end
|
51
|
+
|
52
|
+
def vpsadmin_network_containers
|
53
|
+
api = vpsadmin_client
|
54
|
+
networks = api.network.list
|
55
|
+
data = {}
|
56
|
+
|
57
|
+
[4, 6].each do |ip_v|
|
58
|
+
data["ipv#{ip_v}"] = networks.select { |net| net.ip_version == ip_v }.map do |net|
|
59
|
+
{ address: net.address, prefix: net.prefix }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
update_file('vpsadmin/networks/containers.nix') do |f|
|
64
|
+
f.puts(ConfCtl::NixFormat.to_nix(data))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def vpsadmin_client
|
71
|
+
return @vpsadmin_client if @vpsadmin_client
|
72
|
+
|
73
|
+
@vpsadmin_client = VpsFree::Client.new
|
74
|
+
|
75
|
+
user = ask('User name: ') { |q| q.default = nil }.to_s
|
76
|
+
password = ask('Password: ') do |q|
|
77
|
+
q.default = nil
|
78
|
+
q.echo = false
|
79
|
+
end.to_s
|
80
|
+
|
81
|
+
@vpsadmin_client.authenticate(:basic, user:, password:)
|
82
|
+
@vpsadmin_client
|
83
|
+
end
|
84
|
+
|
85
|
+
def update_file(relpath, &)
|
86
|
+
abs = File.join(data_dir, relpath)
|
87
|
+
tmp = "#{abs}.new"
|
88
|
+
|
89
|
+
File.open(tmp, 'w', &)
|
90
|
+
File.rename(tmp, abs)
|
91
|
+
end
|
92
|
+
|
93
|
+
def data_dir
|
94
|
+
@data_dir ||= File.join(ConfCtl::ConfDir.path, 'data')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|