inspec-podman-resources 7.1.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 683c07b27de847a7f6aaed11eb700a1f92922cd19d25fec3d96eb123927ea95a
4
+ data.tar.gz: 076cb1a2d18110451bfe1de149592f124074e1f672c25d74a227e80e21b03518
5
+ SHA512:
6
+ metadata.gz: 357d4ede45d728759c695a494233931efbb3089628447f45e735c38cc25cb98197485137f286fa7f1df1cd81c2ad2db8834a6a33da5727f72a65b8217e806e79
7
+ data.tar.gz: 39bf309cb79cf7fbc1d15c8708c31dedba208bd94fcda5cdc62c86c2962a8208b2b1f3ef70709a7cc31c4daa285fede654161de4a6286751f16c58131022c64a
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+ gem "inspec", git: "https://github.com/inspec/inspec", branch: "inspec-7"
5
+ gem "inspec-bin", git: "https://github.com/inspec/inspec", branch: "inspec-7"
6
+
7
+ gemspec
8
+
9
+ group :test do
10
+ gem "byebug"
11
+ gem "chefstyle"
12
+ gem "minitest"
13
+ gem "m"
14
+ gem "mocha"
15
+ gem "rake"
16
+ gem "simplecov"
17
+ gem "simplecov_json_formatter"
18
+ end
data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # inspec-podman-resources
2
+
3
+ Podman InSpec Resources in a Gem
4
+
5
+ This repository contains the InSpec Podman resources, formerly contained in InSpec Core. In InSpec 7+, these resources are available in a gem, `inspec-podman-resources`.
6
+
7
+ ## Usage
8
+
9
+ To use this resource pack, add this dependency to your inspec.yml :
10
+
11
+ ```yaml
12
+ depends:
13
+ - name: inspec-podman-resources
14
+ gem: inspec-podman-resources
15
+ ```
16
+
17
+
@@ -0,0 +1,44 @@
1
+ # As plugins are usually packaged and distributed as a RubyGem,
2
+ # we have to provide a .gemspec file, which controls the gembuild
3
+ # and publish process. This is a fairly generic gemspec.
4
+
5
+ # It is traditional in a gemspec to dynamically load the current version
6
+ # from a file in the source tree. The next three lines make that happen.
7
+ lib = File.expand_path("lib", __dir__)
8
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
9
+ require "inspec-podman-resources/version"
10
+
11
+ Gem::Specification.new do |spec|
12
+ # Importantly, all InSpec plugins must be prefixed with `inspec-` (most
13
+ # plugins) or `train-` (plugins which add new connectivity features).
14
+ spec.name = "inspec-podman-resources"
15
+
16
+ # It is polite to namespace your plugin under InspecPlugins::YourPluginInCamelCase
17
+ spec.version = InspecPlugins::PodmanResources::VERSION
18
+ spec.authors = ["InSpec Core Maintainers"]
19
+ spec.email = ["inspec@progress.com"]
20
+ spec.summary = "podman InSpec Resources in a Gem"
21
+ spec.description = "Contains InSpec 7.0+ resources fo interacting with Podman Resources."
22
+ spec.homepage = "https://github.com/inspec/inspec-podman-resources"
23
+ spec.license = "Apache-2.0"
24
+
25
+ # Though complicated-looking, this is pretty standard for a gemspec.
26
+ # It just filters what will actually be packaged in the gem (leaving
27
+ # out tests, etc)
28
+ spec.files = %w{
29
+ README.md inspec-podman-resources.gemspec Gemfile inspec.yml
30
+ } + Dir.glob(
31
+ "lib/**/*", File::FNM_DOTMATCH
32
+ ).reject { |f| File.directory?(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ spec.required_ruby_version = ">= 3.1.0"
36
+
37
+ # If you rely on any other gems, list them here with any constraints.
38
+ # This is how `inspec plugin install` is able to manage your dependencies.
39
+ # For example, perhaps you are writing a thing that talks to AWS, and you
40
+ # want to ensure you have `aws-sdk` in a certain version.
41
+
42
+ # This plugin uses InSpec 7 Resource Pack Plugins
43
+ spec.add_dependency "inspec-core", ">= 7.0"
44
+ end
data/inspec.yml ADDED
@@ -0,0 +1,10 @@
1
+ name: inspec-podman-resources
2
+ title: Resource pack gem for PodmanResources
3
+ maintainer: InSpec Core Maintainers
4
+ copyright: Progress Software Corporation
5
+ copyright_email: inspec@progress.com
6
+ license: ApacheV2
7
+ summary: podman InSpec Resources in a Gem
8
+ version: 7.1.2
9
+ supports:
10
+ platform: os
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ # Plugin Definition file
3
+ # The purpose of this file is to declare to InSpec what plugin_types (capabilities)
4
+ # are included in this plugin, and provide activator that will load them as needed.
5
+
6
+ # It is important that this file load successfully and *quickly*.
7
+ # Your plugin's functionality may never be used on this InSpec run; so we keep things
8
+ # fast and light by only loading heavy things when they are needed.
9
+
10
+ # Presumably this is light
11
+ require "inspec-podman-resources/version"
12
+
13
+ # The InspecPlugins namespace is where all plugins should declare themselves.
14
+ # The "Inspec" capitalization is used throughout the InSpec source code; yes, it's
15
+ # strange.
16
+ module InspecPlugins
17
+ # Pick a reasonable namespace here for your plugin. A reasonable choice
18
+ # would be the CamelCase version of your plugin gem name.
19
+ # inspec-test-resources => TestResources
20
+ module PodmanResources
21
+ # This simple class handles the plugin definition, so calling it simply Plugin is OK.
22
+ # Inspec.plugin returns various Classes, intended to be superclasses for various
23
+ # plugin components. Here, the one-arg form gives you the Plugin Definition superclass,
24
+ # which mainly gives you access to the activator / plugin_type DSL.
25
+ # The number '2' says you are asking for version 2 of the plugin API. If there are
26
+ # future versions, InSpec promises plugin API v2 will work for at least two more InSpec
27
+ # major versions.
28
+ class Plugin < ::Inspec.plugin(2)
29
+ # Internal machine name of the plugin. InSpec will use this in errors, etc.
30
+ plugin_name :"inspec-podman-resources"
31
+
32
+ # Define a new Resource Pack.
33
+ resource_pack :"inspec-podman-resources" do
34
+ # This file will load the resources implicitly via the superclass
35
+ require "inspec-podman-resources/resource_pack"
36
+
37
+ # Having loaded our functionality, return a class that represents the plugin.
38
+ # Reserved for future use.
39
+ InspecPlugins::PodmanResources::ResourcePack
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require "inspec/resource"
3
+
4
+ module InspecPlugins::PodmanResources
5
+ # This class will provide the actual CLI implementation.
6
+ # Its superclass is provided by another call to Inspec.plugin,
7
+ # this time with two args. The first arg specifies we are requesting
8
+ # version 2 of the Plugins API. The second says we are making a Resource
9
+ # Pack plugin component, so please make available any DSL needed
10
+ # for that.
11
+ class ResourcePack < Inspec.plugin(2, :resource_pack)
12
+ # TBD
13
+ # load_timing :early <-- isn't that implicit in the rewuire statements
14
+ # train relationship declarations? <-- that should be in the gemspec
15
+ end
16
+ end
@@ -0,0 +1,351 @@
1
+ require "inspec/resources/command"
2
+ require "inspec/utils/filter"
3
+ require "hashie/mash"
4
+
5
+ class Podman < Inspec.resource(1)
6
+ # Resource requires an internal name.
7
+ name "podman"
8
+
9
+ # Restrict to only run on the below platforms (if none were given,
10
+ # all OS's and cloud API's supported)
11
+ supports platform: "unix"
12
+
13
+ desc "A resource to retrieve information about podman"
14
+
15
+ example <<~EXAMPLE
16
+ describe podman.containers do
17
+ its('images') { should include "docker.io/library/ubuntu:latest" }
18
+ end
19
+
20
+ describe podman.images do
21
+ its('names') { should_not include "docker.io/library/ubuntu:latest" }
22
+ end
23
+
24
+ describe podman.pods do
25
+ its("ids") { should include "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc" }
26
+ end
27
+
28
+ describe podman.info.host do
29
+ its("os") { should eq "linux"}
30
+ end
31
+
32
+ describe podman.version do
33
+ its("Client.Version") { should eq "4.1.0"}
34
+ end
35
+
36
+ podman.containers.ids.each do |id|
37
+ # call podman inspect for a specific container id
38
+ describe podman.object(id) do
39
+ its("State.OciVersion") { should eq "1.0.2-dev" }
40
+ its("State.Running") { should eq true}
41
+ end
42
+ end
43
+ EXAMPLE
44
+
45
+ def containers
46
+ PodmanContainerFilter.new(parse_containers)
47
+ end
48
+
49
+ def images
50
+ PodmanImageFilter.new(parse_images)
51
+ end
52
+
53
+ def networks
54
+ PodmanNetworkFilter.new(parse_networks)
55
+ end
56
+
57
+ def pods
58
+ PodmanPodFilter.new(parse_pods)
59
+ end
60
+
61
+ def volumes
62
+ PodmanVolumeFilter.new(parse_volumes)
63
+ end
64
+
65
+ def version
66
+ return @version if defined?(@version)
67
+
68
+ sub_cmd = "version --format json"
69
+ output = run_command(sub_cmd)
70
+ @version = Hashie::Mash.new(JSON.parse(output))
71
+ rescue JSON::ParserError => _e
72
+ Hashie::Mash.new({})
73
+ end
74
+
75
+ def info
76
+ return @info if defined?(@info)
77
+
78
+ sub_cmd = "info --format json"
79
+ output = run_command(sub_cmd)
80
+ @info = Hashie::Mash.new(JSON.parse(output))
81
+ rescue JSON::ParserError => _e
82
+ Hashie::Mash.new({})
83
+ end
84
+
85
+ # returns information about podman objects
86
+ def object(id)
87
+ return @inspect if defined?(@inspect)
88
+
89
+ output = run_command("inspect #{id} --format json")
90
+ data = JSON.parse(output)
91
+ data = data[0] if data.is_a?(Array)
92
+ @inspect = Hashie::Mash.new(data)
93
+ rescue JSON::ParserError => _e
94
+ Hashie::Mash.new({})
95
+ end
96
+
97
+ def to_s
98
+ "Podman"
99
+ end
100
+
101
+ private
102
+
103
+ # Calls the run_command method to get all podman containers and parse the command output.
104
+ # Returns the parsed command output.
105
+ def parse_containers
106
+ labels = %w{ID Image ImageID Command CreatedAt RunningFor Status Pod Ports Size Names Networks Labels Mounts}
107
+ parse_json_command(labels, "ps -a --no-trunc --size")
108
+ end
109
+
110
+ # Calls the run_command method to get all podman images and parse the command output.
111
+ # Returns the parsed command output.
112
+ def parse_images
113
+ labels = %w{ID Repository Tag Size Digest CreatedAt CreatedSince History}
114
+ parse_json_command(labels, "images -a --no-trunc")
115
+ end
116
+
117
+ # Calls the run_command method to get all podman network list and parse the command output.
118
+ # Returns the parsed command output.
119
+ def parse_networks
120
+ labels = %w{ID Name Driver Labels Options IPAMOptions Created Internal IPv6Enabled DNSEnabled NetworkInterface Subnets}
121
+ parse_json_command(labels, "network ls --no-trunc")
122
+ end
123
+
124
+ # Calls the run_command method to get all podman pod list and parse the command output.
125
+ # Returns the parsed command output.
126
+ def parse_pods
127
+ sub_cmd = "pod ps --no-trunc --format json"
128
+ output = run_command(sub_cmd)
129
+ parse(output)
130
+ end
131
+
132
+ # Calls the run_command method to get all podman volume list and parse the command output.
133
+ # Returns the parsed command output.
134
+ def parse_volumes
135
+ sub_cmd = "volume ls --format json"
136
+ output = run_command(sub_cmd)
137
+ parse(output)
138
+ end
139
+
140
+ # Runs the given podman command on the host machine on which podman is installed
141
+ # Returns the command output or raises the command execution error.
142
+ def run_command(subcommand)
143
+ result = inspec.command("podman #{subcommand}")
144
+ if result.stderr.empty?
145
+ result.stdout
146
+ else
147
+ raise "Error while running command \'podman #{subcommand}\' : #{result.stderr}"
148
+ end
149
+ end
150
+
151
+ def parse_json_command(labels, subcommand)
152
+ # build command
153
+ format = labels.map { |label| "\"#{label}\": {{json .#{label}}}" }
154
+ raw = inspec.command("podman #{subcommand} --format '{#{format.join(", ")}}'").stdout
155
+ output = []
156
+
157
+ raw.each_line do |entry|
158
+ # convert all keys to lower_case to work well with ruby and filter table
159
+ row = JSON.parse(entry).map do |key, value|
160
+ [key.downcase, value]
161
+ end.to_h
162
+
163
+ # ensure all keys are there
164
+ row = ensure_keys(row, labels)
165
+ output.push(row)
166
+ end
167
+
168
+ output
169
+ rescue JSON::ParserError => _e
170
+ warn "Could not parse `podman #{subcommand}` output"
171
+ []
172
+ end
173
+
174
+ def ensure_keys(entry, labels)
175
+ labels.each do |key|
176
+ entry[key.downcase] = nil unless entry.key?(key.downcase)
177
+ end
178
+ entry
179
+ end
180
+
181
+ # Method to parse JDON content.
182
+ # Returns: Parsed data.
183
+ def parse(content)
184
+ require "json" unless defined?(JSON)
185
+ output = JSON.parse(content)
186
+ parsed_output = []
187
+ output.each do |entry|
188
+ entry = entry.map do |k, v|
189
+ [k.downcase, v]
190
+ end.to_h
191
+ parsed_output << entry
192
+ end
193
+ parsed_output
194
+ rescue => e
195
+ raise Inspec::Exceptions::ResourceFailed, "Unable to parse command JSON output: #{e.message}"
196
+ end
197
+ end
198
+
199
+ # class for podman.containers plural resource
200
+ class PodmanContainerFilter
201
+ filter = FilterTable.create
202
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
203
+ filter.register_column(:commands, field: "command")
204
+ .register_column(:ids, field: "id")
205
+ .register_column(:created_at, field: "createdat")
206
+ .register_column(:images, field: "image")
207
+ .register_column(:names, field: "names")
208
+ .register_column(:status, field: "status")
209
+ .register_column(:image_ids, field: "image_id")
210
+ .register_column(:labels, field: "labels", style: :simple)
211
+ .register_column(:mounts, field: "mounts")
212
+ .register_column(:networks, field: "networks")
213
+ .register_column(:pods, field: "pod")
214
+ .register_column(:ports, field: "ports")
215
+ .register_column(:sizes, field: "size")
216
+ .register_column(:running_for, field: "running_for")
217
+ .register_custom_matcher(:running?) do |x|
218
+ x.where { status.downcase.start_with?("up") }
219
+ end
220
+ filter.install_filter_methods_on_resource(self, :containers)
221
+
222
+ attr_reader :containers
223
+ def initialize(containers)
224
+ @containers = containers
225
+ end
226
+
227
+ def to_s
228
+ "Podman Containers"
229
+ end
230
+
231
+ def resource_id
232
+ "Podman Containers"
233
+ end
234
+ end
235
+
236
+ # class for podman.images plural resource
237
+ class PodmanImageFilter
238
+ filter = FilterTable.create
239
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
240
+ filter.register_column(:ids, field: "id")
241
+ .register_column(:repositories, field: "repository")
242
+ .register_column(:tags, field: "tag")
243
+ .register_column(:sizes, field: "size")
244
+ .register_column(:digests, field: "digest")
245
+ .register_column(:created_at, field: "createdat")
246
+ .register_column(:created_since, field: "createdsince")
247
+ .register_column(:history, field: "history")
248
+ filter.install_filter_methods_on_resource(self, :images)
249
+
250
+ attr_reader :images
251
+ def initialize(images)
252
+ @images = images
253
+ end
254
+
255
+ def to_s
256
+ "Podman Images"
257
+ end
258
+
259
+ def resource_id
260
+ "Podman Images"
261
+ end
262
+ end
263
+
264
+ class PodmanNetworkFilter
265
+ filter = FilterTable.create
266
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
267
+ .register_column(:ids, field: "id")
268
+ .register_column(:names, field: "name")
269
+ .register_column(:drivers, field: "driver")
270
+ .register_column(:network_interfaces, field: "networkinterface")
271
+ .register_column(:created, field: "created")
272
+ .register_column(:subnets, field: "subnets")
273
+ .register_column(:ipv6_enabled, field: "ipv6enabled")
274
+ .register_column(:internal, field: "internal")
275
+ .register_column(:dns_enabled, field: "dnsenabled")
276
+ .register_column(:ipam_options, field: "ipamoptions")
277
+ .register_column(:options, field: "options")
278
+ .register_column(:labels, field: "labels")
279
+ filter.install_filter_methods_on_resource(self, :networks)
280
+
281
+ attr_reader :networks
282
+ def initialize(networks)
283
+ @networks = networks
284
+ end
285
+
286
+ def to_s
287
+ "Podman Networks"
288
+ end
289
+
290
+ def resource_id
291
+ "Podman Networks"
292
+ end
293
+ end
294
+
295
+ class PodmanPodFilter
296
+ filter = FilterTable.create
297
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
298
+ .register_column(:ids, field: "id")
299
+ .register_column(:cgroups, field: "cgroup")
300
+ .register_column(:containers, field: "containers")
301
+ .register_column(:created, field: "created")
302
+ .register_column(:infraids, field: "infraid")
303
+ .register_column(:names, field: "name")
304
+ .register_column(:namespaces, field: "namespace")
305
+ .register_column(:networks, field: "networks")
306
+ .register_column(:status, field: "status")
307
+ .register_column(:labels, field: "labels")
308
+ filter.install_filter_methods_on_resource(self, :pods)
309
+
310
+ attr_reader :pods
311
+ def initialize(pods)
312
+ @pods = pods
313
+ end
314
+
315
+ def to_s
316
+ "Podman Pods"
317
+ end
318
+
319
+ def resource_id
320
+ "Podman Pods"
321
+ end
322
+ end
323
+
324
+ class PodmanVolumeFilter
325
+ filter = FilterTable.create
326
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
327
+ .register_column(:names, field: "name")
328
+ .register_column(:drivers, field: "driver")
329
+ .register_column(:mountpoints, field: "mountpoint")
330
+ .register_column(:createdat, field: "createdat")
331
+ .register_column(:labels, field: "labels")
332
+ .register_column(:scopes, field: "scope")
333
+ .register_column(:options, field: "options")
334
+ .register_column(:mountcount, field: "mountcount")
335
+ .register_column(:needscopyup, field: "needscopyup")
336
+ .register_column(:needschown, field: "needschown")
337
+ filter.install_filter_methods_on_resource(self, :volumes)
338
+
339
+ attr_reader :volumes
340
+ def initialize(volumes)
341
+ @volumes = volumes
342
+ end
343
+
344
+ def to_s
345
+ "Podman Volumes"
346
+ end
347
+
348
+ def resource_id
349
+ "Podman Volumes"
350
+ end
351
+ end
@@ -0,0 +1,82 @@
1
+ require "inspec-podman-resources/resources/podman"
2
+ require "inspec-podman-resources/resources/podman_object"
3
+
4
+ class PodmanContainer < Inspec.resource(1)
5
+ include PodmanObject
6
+
7
+ name "podman_container"
8
+ supports platform: "unix"
9
+
10
+ desc "Inspec core resource to retrieve information about podman container"
11
+
12
+ example <<~EXAMPLE
13
+ describe podman_container("sweet_mendeleev") do
14
+ it { should exist }
15
+ it { should be_running }
16
+ its("id") { should eq "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" }
17
+ its("image") { should eq "docker.io/library/nginx:latest" }
18
+ its("labels") { should include "maintainer"=>"NGINX Docker Maintainers <docker-maint@nginx.com>" }
19
+ its("ports") { should eq nil }
20
+ end
21
+
22
+ describe podman_container(id: "591270d8d80d2667") do
23
+ it { should exist }
24
+ it { should be_running }
25
+ end
26
+ EXAMPLE
27
+
28
+ def initialize(opts = {})
29
+ skip_resource "The `podman_container` resource is not yet available on your OS." unless inspec.os.unix?
30
+
31
+ # if a string is provided, we expect it is the name
32
+ if opts.is_a?(String)
33
+ @opts = { name: opts }
34
+ else
35
+ @opts = opts
36
+ end
37
+ end
38
+
39
+ def running?
40
+ status.downcase.start_with?("up") if object_info.entries.length == 1
41
+ end
42
+
43
+ def status
44
+ object_info.status[0] if object_info.entries.length == 1
45
+ end
46
+
47
+ def labels
48
+ object_info.labels
49
+ end
50
+
51
+ def ports
52
+ object_info.ports[0] if object_info.entries.length == 1
53
+ end
54
+
55
+ def command
56
+ return unless object_info.entries.length == 1
57
+
58
+ object_info.commands[0]
59
+ end
60
+
61
+ def image
62
+ object_info.images[0] if object_info.entries.length == 1
63
+ end
64
+
65
+ def resource_id
66
+ object_info.ids[0] || @opts[:id] || @opts[:name] || ""
67
+ end
68
+
69
+ def to_s
70
+ name = @opts[:name] || @opts[:id]
71
+ "Podman Container #{name}"
72
+ end
73
+
74
+ private
75
+
76
+ def object_info
77
+ return @info if defined?(@info)
78
+
79
+ opts = @opts
80
+ @info = inspec.podman.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
81
+ end
82
+ end
@@ -0,0 +1,105 @@
1
+ require "inspec/resources/command"
2
+ require "inspec-podman-resources/resources/podman_object"
3
+ require "inspec-podman-resources/resources/utils/podman"
4
+
5
+ class PodmanImage < Inspec.resource(1)
6
+ include PodmanObject
7
+ include Utils::Podman
8
+ name "podman_image"
9
+ supports platform: "unix"
10
+
11
+ desc "InSpec core resource to retrieve information about podman image"
12
+
13
+ example <<~EXAMPLE
14
+ describe podman_image("docker.io/library/busybox") do
15
+ it { should exist }
16
+ its("repo_tags") { should include "docker.io/library/busybox:latest" }
17
+ its("size") { should eq 1636053 }
18
+ its("resource_id") { should eq "docker.io/library/busybox:latest" }
19
+ end
20
+
21
+ describe podman_image("docker.io/library/busybox:latest") do
22
+ it { should exist }
23
+ end
24
+
25
+ describe podman_image(repo: "docker.io/library/busybox", tag: "latest") do
26
+ it { should exist }
27
+ end
28
+
29
+ describe podman_image(id: "3c19bafed223") do
30
+ it { should exist }
31
+ end
32
+ EXAMPLE
33
+
34
+ attr_reader :opts, :image_info
35
+
36
+ def initialize(opts)
37
+ skip_resource "The `podman_image` resource is not yet available on your OS." unless inspec.os.unix?
38
+ opts = { image: opts } if opts.is_a?(String)
39
+ @opts = sanitize_options(opts)
40
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
41
+
42
+ @image_info = get_image_info
43
+ end
44
+
45
+ LABELS = {
46
+ "id" => "ID",
47
+ "repo_tags" => "RepoTags",
48
+ "size" => "Size",
49
+ "digest" => "Digest",
50
+ "created_at" => "Created",
51
+ "version" => "Version",
52
+ "names_history" => "NamesHistory",
53
+ "repo_digests" => "RepoDigests",
54
+ "architecture" => "Architecture",
55
+ "os" => "Os",
56
+ "virtual_size" => "VirtualSize",
57
+ }.freeze
58
+
59
+ ## This creates all the required properties methods dynamically.
60
+ LABELS.each do |k, v|
61
+ define_method(k) do
62
+ image_info[k.to_s]
63
+ end
64
+ end
65
+
66
+ def exist?
67
+ ! image_info.empty?
68
+ end
69
+
70
+ def resource_id
71
+ opts[:id] || opts[:image] || ""
72
+ end
73
+
74
+ def to_s
75
+ "podman_image #{resource_id}"
76
+ end
77
+
78
+ private
79
+
80
+ def sanitize_options(opts)
81
+ opts.merge!(parse_components_from_image(opts[:image]))
82
+
83
+ # assume a "latest" tag if we don't have one
84
+ opts[:tag] ||= "latest"
85
+
86
+ # Assemble/reassemble the image from the repo and tag
87
+ opts[:image] = "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil?
88
+
89
+ opts
90
+ end
91
+
92
+ def get_image_info
93
+ current_image = opts[:id] || opts[:image] || opts[:repo] + ":" + opts[:tag]
94
+ json_key_label = generate_go_template(LABELS)
95
+ podman_inspect_cmd = inspec.command("podman image inspect #{current_image} --format '{#{json_key_label}}'")
96
+
97
+ if podman_inspect_cmd.exit_status == 0
98
+ parse_command_output(podman_inspect_cmd.stdout)
99
+ elsif podman_inspect_cmd.stderr =~ /failed to find image/
100
+ {}
101
+ else
102
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman image information for #{current_image}.\nError message: #{podman_inspect_cmd.stderr}"
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,80 @@
1
+ require "inspec/resources/command"
2
+ require "inspec-podman-resources/resources/utils/podman"
3
+
4
+ class PodmanNetwork < Inspec.resource(1)
5
+ include Utils::Podman
6
+
7
+ name "podman_network"
8
+
9
+ supports platform: "unix"
10
+
11
+ desc "InSpec core resource to retrive information about the given Podman network"
12
+
13
+ example <<~EXAMPLE
14
+ describe podman_network("podman") do
15
+ it { should exist }
16
+ end
17
+ describe podman_network("3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f") do
18
+ its("driver") { should eq bridge }
19
+ end
20
+ EXAMPLE
21
+
22
+ LABELS = {
23
+ id: "ID",
24
+ name: "Name",
25
+ driver: "Driver",
26
+ labels: "Labels",
27
+ options: "Options",
28
+ ipam_options: "IPAMOptions",
29
+ internal: "Internal",
30
+ created: "Created",
31
+ ipv6_enabled: "IPv6Enabled",
32
+ dns_enabled: "DNSEnabled",
33
+ network_interface: "NetworkInterface",
34
+ subnets: "Subnets",
35
+ }.freeze
36
+
37
+ attr_reader :param, :network_info
38
+ def initialize(param)
39
+ skip_resource "The `podman_network` resource is not yet available on your OS." unless inspec.os.unix?
40
+
41
+ @param = param
42
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
43
+
44
+ @network_info = get_network_info
45
+ end
46
+
47
+ ## This creates all the required properties methods dynamically.
48
+ LABELS.each do |k, v|
49
+ define_method(k) do
50
+ network_info[k.to_s]
51
+ end
52
+ end
53
+
54
+ def exist?
55
+ !network_info.empty?
56
+ end
57
+
58
+ def resource_id
59
+ id || param || ""
60
+ end
61
+
62
+ def to_s
63
+ "podman_network #{resource_id}"
64
+ end
65
+
66
+ private
67
+
68
+ def get_network_info
69
+ go_template_format = generate_go_template(LABELS)
70
+ result = inspec.command("podman network inspect #{param} --format '{#{go_template_format}}'")
71
+
72
+ if result.exit_status == 0
73
+ parse_command_output(result.stdout)
74
+ elsif result.stderr =~ /network not found/
75
+ {}
76
+ else
77
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman network information for #{param}.\nError message: #{result.stderr}"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module PodmanObject
3
+ def exist?
4
+ object_info.exists?
5
+ end
6
+
7
+ def id
8
+ object_info.ids[0] if object_info.entries.size == 1
9
+ end
10
+
11
+ private
12
+
13
+ def parse_components_from_image(image_string)
14
+ # if the user did not supply an image string, they likely supplied individual
15
+ # option parameters, such as repo and tag. Return empty data back to the caller.
16
+ return {} if image_string.nil?
17
+
18
+ first_colon = image_string.index(":") || -1
19
+ first_slash = image_string.index("/") || -1
20
+
21
+ if image_string.count(":") == 2
22
+ # If there are two colons in the image string, it contains a repo-with-port and a tag.
23
+ # example: localhost:5000/chef/inspec:1.46.3
24
+ partitioned_string = image_string.rpartition(":")
25
+ repo = partitioned_string.first
26
+ tag = partitioned_string.last
27
+ image_name = repo.split("/")[1..-1].join
28
+ elsif image_string.count(":") == 1 && first_colon < first_slash
29
+ # If there's one colon in the image string, and it comes before a forward-slash,
30
+ # it contains a repo-with-port but no tag.
31
+ # example: localhost:5000/ubuntu
32
+ repo = image_string
33
+ tag = nil
34
+ image_name = repo.split("/")[1..-1].join
35
+ else
36
+ # If there's one colon in the image string and it doesn't preceed a slash, or if
37
+ # there is no colon at all, then it separates the repo from the tag, if there is a tag.
38
+ # example: chef/inspec:1.46.3
39
+ # example: chef/inspec
40
+ # example: ubuntu:14.04
41
+ repo, tag = image_string.split(":")
42
+ image_name = repo
43
+ end
44
+
45
+ # return the repo, image_name and tag parsed from the string, which can be merged into
46
+ # the rest of the user-supplied options
47
+ { repo: repo, image_name: image_name, tag: tag }
48
+ end
49
+ end
@@ -0,0 +1,98 @@
1
+ require "inspec/resources/command"
2
+ require "inspec-podman-resources/resources/utils/podman"
3
+
4
+ class PodmanPod < Inspec.resource(1)
5
+ include Utils::Podman
6
+
7
+ name "podman_pod"
8
+ supports platform: "unix"
9
+
10
+ desc "InSpec core resource to retrieve information about podman pod"
11
+
12
+ example <<~EXAMPLE
13
+ describe podman_pod("nginx-frontend") do
14
+ it { should exist }
15
+ its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" }
16
+ its("name") { should eq "nginx-frontend" }
17
+ its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" }
18
+ its("create_command") { should include "new:nginx-frontend" }
19
+ its("state") { should eq "Running" }
20
+ its("hostname") { should eq "" }
21
+ its("create_cgroup") { should eq true }
22
+ its("cgroup_parent") { should eq "user.slice" }
23
+ its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" }
24
+ its("create_infra") { should eq true }
25
+ its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" }
26
+ its("infra_config") { should include "DNSOption" }
27
+ its("shared_namespaces") { should include "ipc" }
28
+ its("num_containers") { should eq 2 }
29
+ its("containers") { should_not be nil }
30
+ end
31
+
32
+ describe podman_pod("non-existing-pod") do
33
+ it { should_not exist }
34
+ end
35
+ EXAMPLE
36
+
37
+ attr_reader :pod_info, :pod_id
38
+
39
+ def initialize(pod_id)
40
+ skip_resource "The `podman_pod` resource is not yet available on your OS." unless inspec.os.unix?
41
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
42
+
43
+ @pod_id = pod_id
44
+ @pod_info = get_pod_info
45
+ end
46
+
47
+ LABELS = {
48
+ "id" => "ID",
49
+ "name" => "Name",
50
+ "created_at" => "Created",
51
+ "create_command" => "CreateCommand",
52
+ "state" => "State",
53
+ "hostname" => "Hostname",
54
+ "create_cgroup" => "CreateCgroup",
55
+ "cgroup_parent" => "CgroupParent",
56
+ "cgroup_path" => "CgroupPath",
57
+ "create_infra" => "CreateInfra",
58
+ "infra_container_id" => "InfraContainerID",
59
+ "infra_config" => "InfraConfig",
60
+ "shared_namespaces" => "SharedNamespaces",
61
+ "num_containers" => "NumContainers",
62
+ "containers" => "Containers",
63
+ }.freeze
64
+
65
+ # This creates all the required properties methods dynamically.
66
+ LABELS.each do |k, _|
67
+ define_method(k) do
68
+ pod_info[k.to_s]
69
+ end
70
+ end
71
+
72
+ def exist?
73
+ !pod_info.empty?
74
+ end
75
+
76
+ def resource_id
77
+ pod_id
78
+ end
79
+
80
+ def to_s
81
+ "Podman Pod #{resource_id}"
82
+ end
83
+
84
+ private
85
+
86
+ def get_pod_info
87
+ json_key_label = generate_go_template(LABELS)
88
+ inspect_pod_cmd = inspec.command("podman pod inspect #{pod_id} --format '{#{json_key_label}}'")
89
+
90
+ if inspect_pod_cmd.exit_status == 0
91
+ parse_command_output(inspect_pod_cmd.stdout)
92
+ elsif inspect_pod_cmd.stderr =~ /no pod with name or ID/
93
+ {}
94
+ else
95
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman pod information for #{pod_id}.\nError message: #{inspect_pod_cmd.stderr}"
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,85 @@
1
+ require "inspec/resources/command"
2
+ require "inspec-podman-resources/resources/utils/podman"
3
+
4
+ class PodmanVolume < Inspec.resource(1)
5
+ include Utils::Podman
6
+
7
+ name "podman_volume"
8
+ supports platform: "unix"
9
+
10
+ desc "InSpec core resource to retrieve information about podman volume"
11
+
12
+ example <<~EXAMPLE
13
+ describe podman_volume("my_volume") do
14
+ it { should exist }
15
+ its("name") { should eq "my_volume" }
16
+ its("driver") { should eq "local" }
17
+ its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" }
18
+ its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" }
19
+ its("labels") { should eq({}) }
20
+ its("scope") { should eq "local" }
21
+ its("options") { should eq({}) }
22
+ its("mount_count") { should eq 0 }
23
+ its("needs_copy_up") { should eq true }
24
+ its("needs_chown") { should eq true }
25
+ end
26
+ EXAMPLE
27
+
28
+ attr_reader :volume_info, :volume_name
29
+
30
+ def initialize(volume_name)
31
+ skip_resource "The `podman_volume` resource is not yet available on your OS." unless inspec.os.unix?
32
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
33
+
34
+ @volume_name = volume_name
35
+ @volume_info = get_volume_info
36
+ end
37
+
38
+ LABELS = {
39
+ "name" => "Name",
40
+ "driver" => "Driver",
41
+ "mountpoint" => "Mountpoint",
42
+ "created_at" => "CreatedAt",
43
+ "labels" => "Labels",
44
+ "scope" => "Scope",
45
+ "options" => "Options",
46
+ "mount_count" => "MountCount",
47
+ "needs_copy_up" => "NeedsCopyUp",
48
+ "needs_chown" => "NeedsChown",
49
+ }.freeze
50
+
51
+ # This creates all the required properties methods dynamically.
52
+ LABELS.each do |k, _|
53
+ define_method(k) do
54
+ volume_info[k.to_s]
55
+ end
56
+ end
57
+
58
+ def exist?
59
+ !volume_info.empty?
60
+ end
61
+
62
+ def resource_id
63
+ volume_name
64
+ end
65
+
66
+ def to_s
67
+ "podman_volume #{resource_id}"
68
+ end
69
+
70
+ private
71
+
72
+ def get_volume_info
73
+ json_key_label = generate_go_template(LABELS)
74
+
75
+ inspect_volume_cmd = inspec.command("podman volume inspect #{volume_name} --format '{#{json_key_label}}'")
76
+
77
+ if inspect_volume_cmd.exit_status == 0
78
+ parse_command_output(inspect_volume_cmd.stdout)
79
+ elsif inspect_volume_cmd.stderr =~ /inspecting object: no such/
80
+ {}
81
+ else
82
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman volume information for #{volume_name}.\nError message: #{inspect_volume_cmd.stderr}"
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,24 @@
1
+
2
+
3
+ require "inspec/resources/command"
4
+
5
+ module Utils
6
+ module Podman
7
+ def podman_running?
8
+ inspec.command("podman version").exit_status == 0
9
+ end
10
+
11
+ # Generates the template in this format using labels hash: "\"id\": {{json .ID}}, \"name\": {{json .Name}}",
12
+ def generate_go_template(labels)
13
+ (labels.map { |k, v| "\"#{k}\": {{json .#{v}}}" }).join(", ")
14
+ end
15
+
16
+ def parse_command_output(output)
17
+ require "json" unless defined?(JSON)
18
+ JSON.parse(output)
19
+ rescue JSON::ParserError => _e
20
+ warn "Could not parse the command output"
21
+ {}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ # This file simply makes it easier for CI engines to update
3
+ # the version stamp, and provide a clean way for the gemspec
4
+ # to learn the current version.
5
+ module InspecPlugins
6
+ module PodmanResources
7
+ VERSION = "7.1.2"
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # This file is known as the "entry point."
3
+ # This is the file InSpec will try to load if it
4
+ # thinks your plugin is installed.
5
+
6
+ # The *only* thing this file should do is setup the
7
+ # load path, then load the plugin definition file.
8
+
9
+ # Next two lines simply add the path of the gem to the load path.
10
+ # This is not needed when being loaded as a gem; but when doing
11
+ # plugin development, you may need it. Either way, it's harmless.
12
+ libdir = File.dirname(__FILE__)
13
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
14
+
15
+ require "inspec-podman-resources/plugin"
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inspec-podman-resources
3
+ version: !ruby/object:Gem::Version
4
+ version: 7.1.2
5
+ platform: ruby
6
+ authors:
7
+ - InSpec Core Maintainers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: inspec-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ description: Contains InSpec 7.0+ resources fo interacting with Podman Resources.
28
+ email:
29
+ - inspec@progress.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - README.md
36
+ - inspec-podman-resources.gemspec
37
+ - inspec.yml
38
+ - lib/inspec-podman-resources.rb
39
+ - lib/inspec-podman-resources/plugin.rb
40
+ - lib/inspec-podman-resources/resource_pack.rb
41
+ - lib/inspec-podman-resources/resources/podman.rb
42
+ - lib/inspec-podman-resources/resources/podman_container.rb
43
+ - lib/inspec-podman-resources/resources/podman_image.rb
44
+ - lib/inspec-podman-resources/resources/podman_network.rb
45
+ - lib/inspec-podman-resources/resources/podman_object.rb
46
+ - lib/inspec-podman-resources/resources/podman_pod.rb
47
+ - lib/inspec-podman-resources/resources/podman_volume.rb
48
+ - lib/inspec-podman-resources/resources/utils/podman.rb
49
+ - lib/inspec-podman-resources/version.rb
50
+ homepage: https://github.com/inspec/inspec-podman-resources
51
+ licenses:
52
+ - Apache-2.0
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.1.0
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubygems_version: 3.3.27
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: podman InSpec Resources in a Gem
73
+ test_files: []