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 +7 -0
- data/Gemfile +18 -0
- data/README.md +17 -0
- data/inspec-podman-resources.gemspec +44 -0
- data/inspec.yml +10 -0
- data/lib/inspec-podman-resources/plugin.rb +43 -0
- data/lib/inspec-podman-resources/resource_pack.rb +16 -0
- data/lib/inspec-podman-resources/resources/podman.rb +351 -0
- data/lib/inspec-podman-resources/resources/podman_container.rb +82 -0
- data/lib/inspec-podman-resources/resources/podman_image.rb +105 -0
- data/lib/inspec-podman-resources/resources/podman_network.rb +80 -0
- data/lib/inspec-podman-resources/resources/podman_object.rb +49 -0
- data/lib/inspec-podman-resources/resources/podman_pod.rb +98 -0
- data/lib/inspec-podman-resources/resources/podman_volume.rb +85 -0
- data/lib/inspec-podman-resources/resources/utils/podman.rb +24 -0
- data/lib/inspec-podman-resources/version.rb +9 -0
- data/lib/inspec-podman-resources.rb +15 -0
- metadata +73 -0
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: []
|