moose-inventory 1.0.7 → 1.0.9
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 +5 -5
- data/.github/workflows/ci.yml +35 -0
- data/.gitignore +1 -1
- data/BACKLOG.md +184 -0
- data/Gemfile.lock +60 -0
- data/README.md +23 -5
- data/bin/moose-inventory +1 -1
- data/docs/release/publishing.md +113 -0
- data/docs/release/release-readiness.md +41 -0
- data/docs/security-audit-2026-05-21.md +71 -0
- data/lib/moose_inventory/cli/formatter.rb +16 -17
- data/lib/moose_inventory/cli/group.rb +1 -1
- data/lib/moose_inventory/cli/group_add.rb +19 -21
- data/lib/moose_inventory/cli/group_addchild.rb +36 -40
- data/lib/moose_inventory/cli/group_addhost.rb +14 -18
- data/lib/moose_inventory/cli/group_addvar.rb +37 -37
- data/lib/moose_inventory/cli/group_get.rb +23 -26
- data/lib/moose_inventory/cli/group_list.rb +12 -15
- data/lib/moose_inventory/cli/group_listvars.rb +12 -14
- data/lib/moose_inventory/cli/group_rm.rb +36 -21
- data/lib/moose_inventory/cli/group_rmchild.rb +5 -6
- data/lib/moose_inventory/cli/group_rmhost.rb +12 -16
- data/lib/moose_inventory/cli/group_rmvar.rb +5 -5
- data/lib/moose_inventory/cli/host.rb +1 -1
- data/lib/moose_inventory/cli/host_add.rb +18 -18
- data/lib/moose_inventory/cli/host_addgroup.rb +9 -9
- data/lib/moose_inventory/cli/host_addvar.rb +6 -6
- data/lib/moose_inventory/cli/host_get.rb +15 -18
- data/lib/moose_inventory/cli/host_list.rb +3 -3
- data/lib/moose_inventory/cli/host_listvars.rb +21 -23
- data/lib/moose_inventory/cli/host_rm.rb +9 -9
- data/lib/moose_inventory/cli/host_rmgroup.rb +5 -5
- data/lib/moose_inventory/cli/host_rmvar.rb +3 -3
- data/lib/moose_inventory/config/config.rb +43 -40
- data/lib/moose_inventory/db/db.rb +70 -50
- data/lib/moose_inventory/db/models.rb +11 -12
- data/lib/moose_inventory/version.rb +1 -1
- data/moose-inventory.gemspec +35 -20
- data/scripts/check.sh +8 -0
- data/scripts/ci/check_permissions.sh +32 -0
- data/scripts/ci/check_security.sh +50 -0
- data/scripts/ci/package_sanity.sh +46 -0
- data/scripts/files.rb +1 -4
- data/scripts/install_dependencies.sh +17 -0
- data/scripts/reports.sh +2 -2
- data/spec/lib/moose_inventory/cli/cli_spec.rb +13 -14
- data/spec/lib/moose_inventory/cli/group_add_spec.rb +118 -119
- data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +49 -51
- data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +80 -83
- data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +91 -91
- data/spec/lib/moose_inventory/cli/group_get_spec.rb +22 -23
- data/spec/lib/moose_inventory/cli/group_list_spec.rb +19 -20
- data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +35 -36
- data/spec/lib/moose_inventory/cli/group_rm_spec.rb +103 -49
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +41 -45
- data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +43 -46
- data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +131 -131
- data/spec/lib/moose_inventory/cli/group_spec.rb +9 -9
- data/spec/lib/moose_inventory/cli/host_add_spec.rb +103 -43
- data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +78 -80
- data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +122 -122
- data/spec/lib/moose_inventory/cli/host_get_spec.rb +16 -16
- data/spec/lib/moose_inventory/cli/host_list_spec.rb +8 -8
- data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +50 -52
- data/spec/lib/moose_inventory/cli/host_rm_spec.rb +12 -12
- data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +48 -51
- data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +136 -136
- data/spec/lib/moose_inventory/config/config_spec.rb +16 -3
- data/spec/lib/moose_inventory/db/db_spec.rb +224 -2
- data/spec/lib/moose_inventory/db/models_spec.rb +10 -11
- data/spec/shared/shared_config_setup.rb +2 -2
- data/spec/spec_helper.rb +7 -8
- metadata +99 -136
- data/.coveralls.yml +0 -0
- data/.rubocop.yml +0 -793
- data/Guardfile +0 -38
- data/config/dotfiles/coveralls.yml +0 -0
- data/config/dotfiles/gitignore +0 -20
- data/config/dotfiles/rubocop.yml +0 -793
- data/scripts/guard_quality.sh +0 -3
- data/scripts/guard_test.sh +0 -2
|
@@ -20,7 +20,7 @@ module Moose
|
|
|
20
20
|
abort('ERROR: Wrong number of arguments, '\
|
|
21
21
|
"#{args.length} for 2 or more.")
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
# Convenience
|
|
25
25
|
db = Moose::Inventory::DB
|
|
26
26
|
fmt = Moose::Inventory::Cli::Formatter
|
|
@@ -34,10 +34,10 @@ module Moose
|
|
|
34
34
|
abort 'ERROR: Cannot manually manipulate the automatic '\
|
|
35
35
|
'group \'ungrouped\'.'
|
|
36
36
|
end
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
# Transaction
|
|
39
39
|
db.transaction do # Transaction start
|
|
40
|
-
puts "Dissociate host '#{name}' from groups '#{groups.join(',')}':"
|
|
40
|
+
puts "Dissociate host '#{name}' from groups '#{groups.join(',')}':"
|
|
41
41
|
fmt.puts 2, "- Retrieve host '#{name}'..."
|
|
42
42
|
host = db.models[:host].find(name: name)
|
|
43
43
|
if host.nil?
|
|
@@ -54,7 +54,7 @@ module Moose
|
|
|
54
54
|
# Check against existing associations
|
|
55
55
|
if groups_ds[name: g].nil?
|
|
56
56
|
fmt.warn "Association {host:#{name} <-> group:#{g}} doesn't exist, skipping.\n"
|
|
57
|
-
fmt.puts 4,
|
|
57
|
+
fmt.puts 4, "- Doesn't exist, skipping."
|
|
58
58
|
else
|
|
59
59
|
group = db.models[:group].find(name: g)
|
|
60
60
|
host.remove_group(group) unless group.nil?
|
|
@@ -66,7 +66,7 @@ module Moose
|
|
|
66
66
|
if host.groups_dataset.count == 0
|
|
67
67
|
fmt.puts 2, '- Add automatic association '\
|
|
68
68
|
"{host:#{name} <-> group:ungrouped}..."
|
|
69
|
-
ungrouped
|
|
69
|
+
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
70
70
|
host.add_group(ungrouped) unless ungrouped.nil?
|
|
71
71
|
fmt.puts 4, '- OK'
|
|
72
72
|
end
|
|
@@ -13,7 +13,7 @@ module Moose
|
|
|
13
13
|
#==========================
|
|
14
14
|
desc 'rmvar', 'Remove a variable from the host'
|
|
15
15
|
# rubocop:disable Metrics/LineLength
|
|
16
|
-
def rmvar(*args) # rubocop:disable Metrics/
|
|
16
|
+
def rmvar(*args) # rubocop:disable Metrics/AbcSize
|
|
17
17
|
# rubocop:enableMetrics/LineLength
|
|
18
18
|
if args.length < 2
|
|
19
19
|
abort('ERROR: Wrong number of arguments, ' \
|
|
@@ -30,8 +30,8 @@ module Moose
|
|
|
30
30
|
|
|
31
31
|
# Transaction
|
|
32
32
|
db.transaction do # Transaction start
|
|
33
|
-
puts "Remove variable(s) '#{vars.join(
|
|
34
|
-
|
|
33
|
+
puts "Remove variable(s) '#{vars.join(',')}' from host '#{name}':"
|
|
34
|
+
|
|
35
35
|
fmt.puts 2, "- retrieve host '#{name}'..."
|
|
36
36
|
host = db.models[:host].find(name: name)
|
|
37
37
|
if host.nil?
|
|
@@ -41,19 +41,19 @@ module Moose
|
|
|
41
41
|
# Default is to search standard locations.
|
|
42
42
|
#
|
|
43
43
|
# --env ENV => sets the section to be used as the configuration.
|
|
44
|
-
# Defaults to "", which forces the use of the
|
|
45
|
-
# defaultenv parameter from the general section of
|
|
46
|
-
# the config file.
|
|
44
|
+
# Defaults to "", which forces the use of the
|
|
45
|
+
# defaultenv parameter from the general section of
|
|
46
|
+
# the config file.
|
|
47
47
|
#
|
|
48
48
|
# --format FORMAT=> See formatter for supported types.
|
|
49
49
|
# Defaults to json.
|
|
50
50
|
#
|
|
51
51
|
# -- trace => Enable more complete exceptions for db transactions
|
|
52
|
-
# Default is not to trace.
|
|
52
|
+
# Default is not to trace.
|
|
53
53
|
|
|
54
54
|
@_confopts = { env: '', format: 'json', ansible: false, trace: false }
|
|
55
55
|
|
|
56
|
-
# Check for two-part flags
|
|
56
|
+
# Check for two-part flags
|
|
57
57
|
%w(config env format).each do |var|
|
|
58
58
|
@_argv.each_with_index do |val, index|
|
|
59
59
|
next if val != "--#{var}"
|
|
@@ -62,7 +62,7 @@ module Moose
|
|
|
62
62
|
break
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
# Check for one-part flags
|
|
67
67
|
%w(ansible trace).each do |var|
|
|
68
68
|
@_argv.each_with_index do |val, index|
|
|
@@ -72,59 +72,58 @@ module Moose
|
|
|
72
72
|
break
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
# Sanity
|
|
77
|
-
# - Ansible output format must be json - pjson is permitted, but yaml is not.
|
|
77
|
+
# - Ansible output format must be json - pjson is permitted, but yaml is not.
|
|
78
78
|
if @_confopts[:ansible] == true
|
|
79
|
-
unless @_confopts[:format] =~ /p|pjson|j|json/
|
|
79
|
+
unless @_confopts[:format] =~ /p|pjson|j|json/
|
|
80
80
|
@_confopts[:format] = 'json'
|
|
81
|
-
end
|
|
81
|
+
end
|
|
82
82
|
end
|
|
83
|
-
|
|
84
83
|
end
|
|
85
84
|
|
|
86
85
|
#----------------------
|
|
87
86
|
def self.top_level_help
|
|
88
87
|
if @_argv[0] == 'help'
|
|
89
|
-
puts
|
|
90
|
-
printf
|
|
91
|
-
printf
|
|
92
|
-
printf
|
|
93
|
-
printf
|
|
94
|
-
printf
|
|
88
|
+
puts 'Global flags:'
|
|
89
|
+
printf ' %-31s %-10s', '--ansible', "# Force Ansible mode (automatically set when using ansible flags)\n"
|
|
90
|
+
printf ' %-31s %-10s', '--config FILE', "# Specifies a configuration file to use\n"
|
|
91
|
+
printf ' %-31s %-10s', '--env ENV', "# Specifies the environment section of the config to use\n"
|
|
92
|
+
printf ' %-31s %-10s', '--format yaml|json|pjson', "# Format for the output of 'get', 'list', and 'listvars' subcommands\n"
|
|
93
|
+
printf ' %-31s %-10s', '--trace', "# Enable more complete exception dumps for database transactions\n"
|
|
95
94
|
puts "\nAnsible flags:"
|
|
96
|
-
printf
|
|
97
|
-
printf
|
|
95
|
+
printf ' %-31s %-10s', '--host HOSTNAME', "# Retrieves host variables for the specified host (alias for 'host listvars HOSTNAME')\n"
|
|
96
|
+
printf ' %-31s %-10s', '--list', "# Retrieves the list of groups (alias for 'group list')\n\n"
|
|
98
97
|
end
|
|
99
98
|
end
|
|
100
|
-
|
|
99
|
+
|
|
101
100
|
#----------------------
|
|
102
|
-
def self.ansible_args
|
|
101
|
+
def self.ansible_args
|
|
103
102
|
#
|
|
104
103
|
# See http://docs.ansible.com/developing_inventory.html for Ansible specs
|
|
105
104
|
# for dynamic inventory sources
|
|
106
|
-
|
|
105
|
+
|
|
107
106
|
# --list => group list
|
|
108
107
|
# --host HOSTNAME => host getvars HOSTNAME
|
|
109
108
|
|
|
110
109
|
case @_argv[0]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
end
|
|
110
|
+
when '--list'
|
|
111
|
+
@_confopts[:ansible] = true
|
|
112
|
+
@_confopts[:format] = 'json' unless @_confopts[:format] =~ /p|pjson|j|json/
|
|
113
|
+
@_argv.clear
|
|
114
|
+
@_argv.concat(%w(group list)).flatten
|
|
115
|
+
when '--host'
|
|
116
|
+
@_confopts[:ansible] = true
|
|
117
|
+
@_confopts[:format] = 'json' unless @_confopts[:format] =~ /p|pjson|j|json/
|
|
118
|
+
host = @_argv[1]
|
|
119
|
+
@_argv.clear
|
|
120
|
+
@_argv.concat(['host', 'listvars', host.to_s]).flatten
|
|
121
|
+
end
|
|
123
122
|
end
|
|
124
123
|
|
|
125
124
|
#----------------------
|
|
126
|
-
def self.resolve_config_file
|
|
127
|
-
if
|
|
125
|
+
def self.resolve_config_file
|
|
126
|
+
if !@_confopts[:config].nil?
|
|
128
127
|
path = File.expand_path(@_confopts[:config])
|
|
129
128
|
if File.exist?(path)
|
|
130
129
|
@_confopts[:config] = path
|
|
@@ -135,8 +134,7 @@ module Moose
|
|
|
135
134
|
possibles = ['./.moose-tools/inventory/config',
|
|
136
135
|
'~/.moose-tools/inventory/config',
|
|
137
136
|
'~/local/etc/moose-tools/inventory/config',
|
|
138
|
-
'/etc/moose-tools/inventory/config'
|
|
139
|
-
]
|
|
137
|
+
'/etc/moose-tools/inventory/config']
|
|
140
138
|
possibles.each do |f|
|
|
141
139
|
file = File.expand_path(f)
|
|
142
140
|
@_confopts[:config] = file if File.exist?(file)
|
|
@@ -170,7 +168,12 @@ module Moose
|
|
|
170
168
|
# rubocop:disable PerceivedComplexity
|
|
171
169
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
|
172
170
|
def self.load
|
|
173
|
-
newsets = symbolize_keys(YAML.
|
|
171
|
+
newsets = symbolize_keys(YAML.safe_load_file(
|
|
172
|
+
@_confopts[:config],
|
|
173
|
+
aliases: false,
|
|
174
|
+
permitted_classes: [],
|
|
175
|
+
permitted_symbols: []
|
|
176
|
+
))
|
|
174
177
|
|
|
175
178
|
path = @_confopts[:config]
|
|
176
179
|
|
|
@@ -184,7 +187,7 @@ module Moose
|
|
|
184
187
|
env = @_confopts[:env]
|
|
185
188
|
@_settings[:config] = newsets[@_confopts[:env].to_sym]
|
|
186
189
|
else
|
|
187
|
-
env
|
|
190
|
+
env = @_settings[:general][:defaultenv]
|
|
188
191
|
(env.nil? || env.empty?) && fail("No defaultenv set in #{path}")
|
|
189
192
|
@_settings[:config] = newsets[env.to_sym]
|
|
190
193
|
end
|
|
@@ -22,6 +22,8 @@ module Moose
|
|
|
22
22
|
|
|
23
23
|
#----------------------
|
|
24
24
|
def self.init
|
|
25
|
+
init_exceptions
|
|
26
|
+
|
|
25
27
|
# If we allow init more than once, then the db connection is remade,
|
|
26
28
|
# which changes Sequel:DATABASES[0], thereby invalidating the sequel
|
|
27
29
|
# models. This causes unexpected behavour. That is to say, because
|
|
@@ -31,8 +33,8 @@ module Moose
|
|
|
31
33
|
# So, we allow init only once, gated by whether @db is nil. In effect,
|
|
32
34
|
# this means we pool the DB connection for the life of the application.
|
|
33
35
|
# Again, not a problem for our one-shot app, but it may be an issue in
|
|
34
|
-
# long-running code. Personally, I don't like this pooling regime -
|
|
35
|
-
# perhaps I'm not understanding how it's supposed to be used?
|
|
36
|
+
# long-running code. Personally, I don't like this pooling regime -
|
|
37
|
+
# perhaps I'm not understanding how it's supposed to be used?
|
|
36
38
|
#
|
|
37
39
|
# QUESTION: can the models be refreshed, to make then again valid? What if
|
|
38
40
|
# we "load" instead of "require" the models?
|
|
@@ -57,55 +59,58 @@ module Moose
|
|
|
57
59
|
@models[:group] = Moose::Inventory::DB::Group
|
|
58
60
|
@models[:groupvar] = Moose::Inventory::DB::Groupvar
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
@exceptions[:moose] = Moose::Inventory::DB::MooseDBException
|
|
62
|
+
end
|
|
62
63
|
|
|
64
|
+
#--------------------
|
|
65
|
+
def self.init_exceptions
|
|
66
|
+
@exceptions ||= {}
|
|
67
|
+
@exceptions[:moose] ||= Moose::Inventory::DB::MooseDBException
|
|
63
68
|
end
|
|
64
69
|
|
|
65
70
|
#--------------------
|
|
66
71
|
def self.transaction
|
|
67
72
|
fail('Database connection has not been established') if @db.nil?
|
|
68
|
-
|
|
73
|
+
|
|
69
74
|
tries = 0
|
|
70
|
-
|
|
75
|
+
|
|
71
76
|
begin
|
|
72
77
|
@db.transaction(savepoint: true) do
|
|
73
78
|
yield
|
|
74
79
|
end
|
|
75
80
|
|
|
76
81
|
rescue Sequel::DatabaseError => e
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
# We want to rescue Sqlite3::BusyException. But, sequel catches that
|
|
83
|
+
# and re-raises it as Sequel::DatabaseError, with a message referencing
|
|
84
|
+
# the original exception class
|
|
85
|
+
|
|
86
|
+
# We look into e, to see whether it is a BusyException. If not,
|
|
87
|
+
# we re-raise immediately.
|
|
88
|
+
raise unless e.message.include?('BusyException')
|
|
89
|
+
|
|
90
|
+
# Looks like a BusyException, so we retry, after a random delay.
|
|
91
|
+
tries += 1
|
|
92
|
+
case tries
|
|
93
|
+
when 1..10
|
|
94
|
+
if Moose::Inventory::Config._confopts[:trace] == true
|
|
95
|
+
STDERR.puts e.message
|
|
96
|
+
end
|
|
97
|
+
sleep rand
|
|
98
|
+
retry
|
|
99
|
+
else
|
|
100
|
+
warn('The database appears to be locked by another process, and '\
|
|
101
|
+
" did not become free after #{tries} tries. Giving up. ")
|
|
102
|
+
|
|
103
|
+
# TODO: Some useful advice to the user, as to what to do about this error.
|
|
104
|
+
raise
|
|
91
105
|
end
|
|
92
|
-
sleep rand()
|
|
93
|
-
retry
|
|
94
|
-
else
|
|
95
|
-
warn('The database appears to be locked by another process, and '\
|
|
96
|
-
" did not become free after #{tries} tries. Giving up. ")
|
|
97
106
|
|
|
98
|
-
# TODO: Some useful advice to the user, as to what to do about this error.
|
|
99
|
-
raise
|
|
100
|
-
end
|
|
101
|
-
|
|
102
107
|
rescue @exceptions[:moose] => e
|
|
103
108
|
warn 'An error occurred during a transaction, any changes have been rolled back.'
|
|
104
109
|
|
|
105
110
|
if Moose::Inventory::Config._confopts[:trace] == true
|
|
106
|
-
STDERR.puts
|
|
111
|
+
STDERR.puts $ERROR_INFO.backtrace
|
|
107
112
|
abort("ERROR: #{e}")
|
|
108
|
-
else
|
|
113
|
+
else
|
|
109
114
|
abort("ERROR: #{e.message}")
|
|
110
115
|
end
|
|
111
116
|
|
|
@@ -158,7 +163,7 @@ module Moose
|
|
|
158
163
|
|
|
159
164
|
else
|
|
160
165
|
@db.drop_table(:hosts, :hostvars,
|
|
161
|
-
:groups,
|
|
166
|
+
:groups, :groupvars, :group_hosts,
|
|
162
167
|
if_exists: true, cascade: true)
|
|
163
168
|
end
|
|
164
169
|
end
|
|
@@ -195,7 +200,7 @@ module Moose
|
|
|
195
200
|
foreign_key :child_id, :groups
|
|
196
201
|
end
|
|
197
202
|
end
|
|
198
|
-
|
|
203
|
+
|
|
199
204
|
unless @db.table_exists? :groupvars
|
|
200
205
|
@db.create_table(:groupvars) do
|
|
201
206
|
primary_key :id
|
|
@@ -225,28 +230,29 @@ module Moose
|
|
|
225
230
|
when 'sqlite3'
|
|
226
231
|
init_sqlite3
|
|
227
232
|
|
|
228
|
-
when '
|
|
233
|
+
when 'mysql'
|
|
229
234
|
init_mysql
|
|
230
235
|
|
|
231
236
|
when 'postgresql'
|
|
232
237
|
init_postgresql
|
|
233
238
|
|
|
234
239
|
else
|
|
235
|
-
fail @exceptions[:moose
|
|
236
|
-
|
|
240
|
+
fail @exceptions[:moose],
|
|
241
|
+
"database adapter #{adapter} is not yet supported."
|
|
237
242
|
end
|
|
238
243
|
end
|
|
239
244
|
|
|
240
245
|
#--------------------
|
|
241
246
|
def self.init_sqlite3 # rubocop:disable Metrics/AbcSize
|
|
242
247
|
require 'sqlite3'
|
|
248
|
+
require 'fileutils'
|
|
243
249
|
|
|
244
250
|
# Quick check that expected keys are at least present & sensible
|
|
245
251
|
config = Moose::Inventory::Config._settings[:config][:db]
|
|
246
252
|
[:file].each do |key|
|
|
247
253
|
if config[key].nil?
|
|
248
|
-
fail @exceptions[:moose
|
|
249
|
-
|
|
254
|
+
fail @exceptions[:moose],
|
|
255
|
+
"Expected key #{key} missing in sqlite3 configuration"
|
|
250
256
|
end
|
|
251
257
|
end
|
|
252
258
|
config[:file].empty? && fail("SQLite3 DB 'file' cannot be empty")
|
|
@@ -254,7 +260,7 @@ module Moose
|
|
|
254
260
|
# Make sure the directory exists
|
|
255
261
|
dbfile = File.expand_path(config[:file])
|
|
256
262
|
dbdir = File.dirname(dbfile)
|
|
257
|
-
|
|
263
|
+
FileUtils.mkdir_p(dbdir) unless Dir.exist?(dbdir)
|
|
258
264
|
|
|
259
265
|
# Create and/or open the database file
|
|
260
266
|
@db = Sequel.sqlite(dbfile)
|
|
@@ -262,26 +268,40 @@ module Moose
|
|
|
262
268
|
|
|
263
269
|
#--------------------
|
|
264
270
|
def self.init_mysql
|
|
265
|
-
require '
|
|
271
|
+
require 'mysql2'
|
|
266
272
|
|
|
267
|
-
#
|
|
268
|
-
|
|
269
|
-
|
|
273
|
+
# Quick check that expected keys are at least present
|
|
274
|
+
config = Moose::Inventory::Config._settings[:config][:db]
|
|
275
|
+
[:host, :database, :user, :password].each do |key|
|
|
276
|
+
if config[key].nil?
|
|
277
|
+
fail @exceptions[:moose],
|
|
278
|
+
"Expected key #{key} missing in mysql configuration"
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
@db = Sequel.mysql2(user: config[:user],
|
|
283
|
+
password: config[:password],
|
|
284
|
+
host: config[:host],
|
|
285
|
+
database: config[:database])
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
#--------------------
|
|
289
|
+
def self.init_postgresql
|
|
290
|
+
require 'pg'
|
|
270
291
|
|
|
271
292
|
# Quick check that expected keys are at least present
|
|
272
293
|
config = Moose::Inventory::Config._settings[:config][:db]
|
|
273
294
|
[:host, :database, :user, :password].each do |key|
|
|
274
295
|
if config[key].nil?
|
|
275
|
-
fail @exceptions[:moose
|
|
276
|
-
|
|
296
|
+
fail @exceptions[:moose],
|
|
297
|
+
"Expected key #{key} missing in postgresql configuration"
|
|
277
298
|
end
|
|
278
299
|
end
|
|
279
300
|
|
|
280
|
-
@db = Sequel.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
)
|
|
301
|
+
@db = Sequel.postgres(user: config[:user],
|
|
302
|
+
password: config[:password],
|
|
303
|
+
host: config[:host],
|
|
304
|
+
database: config[:database])
|
|
285
305
|
end
|
|
286
306
|
end
|
|
287
307
|
end
|
|
@@ -11,20 +11,19 @@ module Moose
|
|
|
11
11
|
##
|
|
12
12
|
# Model for the groups table
|
|
13
13
|
class Group < Sequel::Model
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
:
|
|
17
|
-
:
|
|
18
|
-
:class=>self
|
|
19
|
-
|
|
20
|
-
many_to_many :children,
|
|
21
|
-
:left_key=>:child_id,
|
|
22
|
-
:right_key=>:parent_id,
|
|
23
|
-
:class=>self
|
|
14
|
+
many_to_many :parents,
|
|
15
|
+
left_key: :parent_id,
|
|
16
|
+
right_key: :child_id,
|
|
17
|
+
class: self
|
|
24
18
|
|
|
25
|
-
many_to_many :
|
|
19
|
+
many_to_many :children,
|
|
20
|
+
left_key: :child_id,
|
|
21
|
+
right_key: :parent_id,
|
|
22
|
+
class: self
|
|
23
|
+
|
|
24
|
+
many_to_many :hosts
|
|
26
25
|
one_to_many :groupvars
|
|
27
|
-
end
|
|
26
|
+
end
|
|
28
27
|
|
|
29
28
|
##
|
|
30
29
|
# Model for the hostvars table
|
data/moose-inventory.gemspec
CHANGED
|
@@ -16,30 +16,45 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
# rubocop:enable Metrics/LineLength
|
|
17
17
|
spec.homepage = 'https://github.com/RusDavies/moose-inventory'
|
|
18
18
|
spec.license = 'MIT'
|
|
19
|
+
spec.required_ruby_version = '>= 3.2'
|
|
19
20
|
|
|
20
21
|
spec.files = `git ls-files -z`.split("\x0")
|
|
21
22
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
22
23
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
23
24
|
spec.require_paths = ['lib']
|
|
24
25
|
|
|
25
|
-
spec.add_runtime_dependency 'indentation', '~> 0.1'
|
|
26
|
-
spec.add_runtime_dependency 'json', '~>1.8'
|
|
27
|
-
spec.add_runtime_dependency 'mysql', '~>2.9'
|
|
28
|
-
#
|
|
29
|
-
spec.add_runtime_dependency '
|
|
30
|
-
spec.add_runtime_dependency '
|
|
31
|
-
spec.add_runtime_dependency '
|
|
32
|
-
spec.add_runtime_dependency '
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
spec.add_development_dependency '
|
|
37
|
-
spec.add_development_dependency '
|
|
38
|
-
spec.add_development_dependency 'guard', '~> 2.12'
|
|
39
|
-
spec.add_development_dependency 'guard-rspec', '~> 4.5'
|
|
40
|
-
spec.add_development_dependency 'guard-rubocop',
|
|
41
|
-
spec.add_development_dependency 'rake', '~> 10.1'
|
|
42
|
-
spec.add_development_dependency 'rspec', '~>3.2'
|
|
43
|
-
spec.add_development_dependency 'rubocop', '>= 0.19'
|
|
44
|
-
spec.add_development_dependency 'simplecov', '~> 0.10'
|
|
26
|
+
# spec.add_runtime_dependency 'indentation', '~> 0.1'
|
|
27
|
+
# spec.add_runtime_dependency 'json', '~>1.8'
|
|
28
|
+
# #spec.add_runtime_dependency 'mysql', '~>2.9' # This causes lots of problems. Need to migrate to the newer mysql2.
|
|
29
|
+
# #spec.add_runtime_dependency 'mysql2', '~>0.3'
|
|
30
|
+
# spec.add_runtime_dependency 'mysql2'
|
|
31
|
+
# spec.add_runtime_dependency 'pg', '~>0.17'
|
|
32
|
+
# spec.add_runtime_dependency 'sequel', '~>4.22'
|
|
33
|
+
# spec.add_runtime_dependency 'sqlite3', '~>1.3'
|
|
34
|
+
# spec.add_runtime_dependency 'thor', '~>0.19'
|
|
35
|
+
# # spec.add_runtime_dependency 'yaml', '~>1.0'
|
|
36
|
+
|
|
37
|
+
# spec.add_development_dependency 'bundler', '~> 1.7'
|
|
38
|
+
# spec.add_development_dependency 'coveralls', '~> 0.8'
|
|
39
|
+
# spec.add_development_dependency 'guard', '~> 2.12'
|
|
40
|
+
# spec.add_development_dependency 'guard-rspec', '~> 4.5'
|
|
41
|
+
# spec.add_development_dependency 'guard-rubocop', '~> 1.2'
|
|
42
|
+
# spec.add_development_dependency 'rake', '~> 10.1'
|
|
43
|
+
# spec.add_development_dependency 'rspec', '~>3.2'
|
|
44
|
+
# spec.add_development_dependency 'rubocop', '>= 0.19'
|
|
45
|
+
# spec.add_development_dependency 'simplecov', '~> 0.10'
|
|
46
|
+
|
|
47
|
+
spec.add_runtime_dependency 'indentation', '~> 0'
|
|
48
|
+
spec.add_runtime_dependency 'json', '>= 2.7', '< 3'
|
|
49
|
+
spec.add_runtime_dependency 'mysql2', '>= 0.5.7', '< 0.6'
|
|
50
|
+
spec.add_runtime_dependency 'pg', '>= 1.5', '< 2'
|
|
51
|
+
spec.add_runtime_dependency 'sequel', '>= 5.80', '< 6'
|
|
52
|
+
spec.add_runtime_dependency 'sqlite3', '>= 1.7', '< 3'
|
|
53
|
+
spec.add_runtime_dependency 'thor', '>= 1.3', '< 2'
|
|
54
|
+
|
|
55
|
+
spec.add_development_dependency 'bundler', '>= 2.2.33', '< 3'
|
|
56
|
+
spec.add_development_dependency 'rake', '>= 13.0', '< 14'
|
|
57
|
+
spec.add_development_dependency 'rspec', '~> 3'
|
|
58
|
+
spec.add_development_dependency 'simplecov', '~> 0'
|
|
59
|
+
|
|
45
60
|
end
|
data/scripts/check.sh
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
allowed_executables=(
|
|
5
|
+
"bin/moose-inventory"
|
|
6
|
+
"scripts/check.sh"
|
|
7
|
+
"scripts/ci/check_permissions.sh"
|
|
8
|
+
"scripts/ci/check_security.sh"
|
|
9
|
+
"scripts/ci/package_sanity.sh"
|
|
10
|
+
"scripts/files.rb"
|
|
11
|
+
"scripts/install_dependencies.sh"
|
|
12
|
+
"scripts/reports.sh"
|
|
13
|
+
"scripts/work-through.sh"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
allowed_file="$(mktemp)"
|
|
17
|
+
actual_file="$(mktemp)"
|
|
18
|
+
trap 'rm -f "$allowed_file" "$actual_file"' EXIT
|
|
19
|
+
|
|
20
|
+
printf '%s\n' "${allowed_executables[@]}" | sort > "$allowed_file"
|
|
21
|
+
|
|
22
|
+
git ls-files -z | while IFS= read -r -d '' path; do
|
|
23
|
+
if [[ -x "$path" ]]; then
|
|
24
|
+
printf '%s\n' "$path"
|
|
25
|
+
fi
|
|
26
|
+
done | sort > "$actual_file"
|
|
27
|
+
|
|
28
|
+
if ! diff -u "$allowed_file" "$actual_file"; then
|
|
29
|
+
echo "Unexpected executable file permissions detected." >&2
|
|
30
|
+
echo "Update scripts/ci/check_permissions.sh only when a new executable entrypoint is intentional." >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
python3 - <<'PY'
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
import urllib.error
|
|
8
|
+
import urllib.request
|
|
9
|
+
|
|
10
|
+
specs = []
|
|
11
|
+
for line in open('Gemfile.lock', encoding='utf-8'):
|
|
12
|
+
item = line.strip()
|
|
13
|
+
if not item or ' (' not in item:
|
|
14
|
+
continue
|
|
15
|
+
if item.startswith('moose-inventory'):
|
|
16
|
+
continue
|
|
17
|
+
name = item.split(' (', 1)[0]
|
|
18
|
+
version = item.split(' (', 1)[1].split(')', 1)[0].split('-', 1)[0]
|
|
19
|
+
if name and name[0].isalpha():
|
|
20
|
+
specs.append((name, version))
|
|
21
|
+
|
|
22
|
+
queries = [
|
|
23
|
+
{'package': {'name': name, 'ecosystem': 'RubyGems'}, 'version': version}
|
|
24
|
+
for name, version in specs
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
request = urllib.request.Request(
|
|
28
|
+
'https://api.osv.dev/v1/querybatch',
|
|
29
|
+
data=json.dumps({'queries': queries}).encode('utf-8'),
|
|
30
|
+
headers={'Content-Type': 'application/json'},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
with urllib.request.urlopen(request, timeout=30) as response:
|
|
35
|
+
data = json.load(response)
|
|
36
|
+
except (urllib.error.URLError, TimeoutError) as exc:
|
|
37
|
+
print(f'OSV dependency check failed: {exc}', file=sys.stderr)
|
|
38
|
+
sys.exit(2)
|
|
39
|
+
|
|
40
|
+
findings = []
|
|
41
|
+
for (name, version), result in zip(specs, data.get('results', [])):
|
|
42
|
+
for vuln in result.get('vulns') or []:
|
|
43
|
+
findings.append((name, version, vuln.get('id', 'unknown'), vuln.get('summary') or ''))
|
|
44
|
+
|
|
45
|
+
print(f'OSV dependency check: queried={len(specs)} vulnerable={len(findings)}')
|
|
46
|
+
if findings:
|
|
47
|
+
for name, version, vuln_id, summary in findings:
|
|
48
|
+
print(f'- {name} {version}: {vuln_id} {summary}', file=sys.stderr)
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
PY
|