Enhance lxc resource to test properties (#6243) (#6275)

* ENHANCE: Extend lxc resource to test the properties

Signed-off-by: Sonu Saha <sonu.saha@progress.com>

* TEST: Extend test for introduced properties

Signed-off-by: Sonu Saha <sonu.saha@progress.com>

* DOCS: Extend test for introduced properties

Signed-off-by: Sonu Saha <sonu.saha@progress.com>

* FIX: Correct exception class to handle bad yaml

Signed-off-by: Sonu Saha <sonu.saha@progress.com>

* Doc Review

Signed-off-by: Deepa Kumaraswamy <dkumaras@progress.com>

* FIX: Handle specific exception to handle bad yaml

Signed-off-by: Sonu Saha <sonu.saha@progress.com>

Signed-off-by: Sonu Saha <sonu.saha@progress.com>
Signed-off-by: Deepa Kumaraswamy <dkumaras@progress.com>
Co-authored-by: Deepa Kumaraswamy <dkumaras@progress.com>

Signed-off-by: Sonu Saha <sonu.saha@progress.com>
Signed-off-by: Deepa Kumaraswamy <dkumaras@progress.com>
Co-authored-by: Sonu Saha <98935583+ahasunos@users.noreply.github.com>
Co-authored-by: Deepa Kumaraswamy <dkumaras@progress.com>
This commit is contained in:
Clinton Wolfe 2022-10-20 14:04:57 -04:00 committed by GitHub
parent 3e3b2c9b5b
commit 9e4c4994c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 231 additions and 30 deletions

View file

@ -11,40 +11,112 @@ platform = "linux"
parent = "inspec/resources/os" parent = "inspec/resources/os"
+++ +++
Use the `lxc` Chef InSpec audit resource to test the information about Linux containers. LXC is a command-line client for LXD that manages your LXD instances (containers and virtual machines). The tests are against the container's information obtained on `lxc info [container-name]`. `lxc` resource allows the testing if the container exists or is in running status. Use the `lxc` Chef InSpec audit resource to test the information about Linux containers. LXC is a command-line client for LXD that manages your LXD instances (containers and virtual machines). The tests are against the container's information obtained on `lxc info [container-name]`. `lxc` resource allows the testing if the container exists or is in *running* status.
## Availability ## Availability
### Installation ### Installation
This resource is distributed with Chef InSpec. This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax ## Syntax
An `lxc` Chef InSpec audit resource allows testing if the container exists or is in running status. An `lxc` Chef InSpec audit resource allows testing if the container exists or is in *running* status.
```ruby
describe lxc("linux-container-name") do describe lxc("linux-container-name") do
it { should exist } it { should exist }
it { should be_running } it { should be_running }
end end
```
## Matchers ## Matchers
For a full list of available matchers, please visit our [matchers page](https://docs.chef.io/inspec/matchers/). For a full list of available matchers, please visit our [matchers page](https://docs.chef.io/inspec/matchers/).
The specific matchers of this resource are: `exist`, `be_running`. The specific matchers of this resource are: `exist` and `be_running`.
### exist ### exist
The `exist` matcher is used to specify if the container exists: The `exist` matcher is used to specify if the container exists:
```ruby
it { should exist } it { should exist }
```
### be_running ### be_running
The `be_running` matcher is used to check if the container is running: The `be_running` matcher is used to check if the container is running:
```ruby
it { should be_running } it { should be_running }
```
## Properties
### name
Returns the instance name.
```ruby
its("name") { should eq "ubuntu-container" }
```
### status
Returns the instance status.
```ruby
its("status") { should cmp "Running" }
```
### type
Returns the instance type (for example, container).
```ruby
its("type") { should eq "container" }
```
### architecture
Returns the architecture of the instance.
```ruby
its("architecture") { should eq "x86_64" }
```
### pid
Returns the pid of the instance.
```ruby
its("pid") { should eq 1378 }
```
### created_at
Returns the creation date of the instance.
```ruby
its("created_at") { should eq "2022/08/16 12:07 UTC" }
```
### last_used_at
Returns the last used date of the instance.
```ruby
its("last_used_at") { should eq "2022/08/17 05:06 UTC" }
```
### resources
Returns the resource information of the instance.
```ruby
its("resources") { should include "Disk usage" }
```
## Examples ## Examples
@ -54,14 +126,37 @@ The following examples show how to use this Chef InSpec audit resource.
The below test passes if the container `immense-phoenix` exists as part of the LXD instances. The below test passes if the container `immense-phoenix` exists as part of the LXD instances.
```ruby
describe lxc("immense-phoenix") do describe lxc("immense-phoenix") do
it { should exist } it { should exist }
end end
```
### Ensures container is in running status ### Ensures container is in running status
The below test passes if the container `delicate-sloth` exists as part of the LXD instances and the status is running. The below test passes if the container `delicate-sloth` exists as part of the LXD instances and the status is running.
```ruby
describe lxc("delicate-sloth") do describe lxc("delicate-sloth") do
it { should be_running } it { should be_running }
end end
```
### Ensures container exists, is in running status, and verifies the different container properties
The below test passes if the container `ubuntu-container` exists, is running, and the properties value matches against the desired value.
```ruby
describe lxc("ubuntu-container") do
it { should exist }
it { should be_running }
its("name") { should eq "ubuntu-container" }
its("status") { should cmp "Running" }
its("type") { should eq "container" }
its("architecture") { should eq "x86_64" }
its("pid") { should eq 1378 }
its("created_at") { should eq "2022/08/16 12:07 UTC" }
its("last_used_at") { should eq "2022/08/17 05:06 UTC" }
its("resources") { should include "Disk usage" }
end
```

View file

@ -9,14 +9,26 @@ module Inspec::Resources
describe lxc("ubuntu-container") do describe lxc("ubuntu-container") do
it { should exist } it { should exist }
it { should be_running } it { should be_running }
its("name") { should eq "ubuntu-container" }
its("status") { should cmp "Running" }
its("type") { should eq "container" }
its("architecture") { should eq "x86_64" }
its("pid") { should eq 1378 }
its("created_at") { should eq "2022/08/16 12:07 UTC" }
its("last_used_at") { should eq "2022/08/17 05:06 UTC" }
its("resources") { should include "Disk usage" }
end end
EXAMPLE EXAMPLE
attr_reader :container_info, :container_name
# Resource initialization. # Resource initialization.
def initialize(container_name) def initialize(container_name)
@container_name = container_name @container_name = container_name
raise Inspec::Exceptions::ResourceSkipped, "The `lxc` resource is not supported on your OS yet." unless inspec.os.linux? raise Inspec::Exceptions::ResourceSkipped, "The `lxc` resource is not supported on your OS yet." unless inspec.os.linux?
@container_info = populate_container_info
end end
def resource_id def resource_id
@ -28,17 +40,60 @@ module Inspec::Resources
end end
def exists? def exists?
lxc_info_cmd.exit_status.to_i == 0 !@container_info.empty?
end end
def running? def running?
container_info = lxc_info_cmd.stdout.split(":").map(&:strip) @container_info.key?("Status") && @container_info["Status"].casecmp("Running") == 0
container_info[0] == "Status" && container_info[1] == "Running" end
def name
@container_info["Name"]
end
def status
@container_info["Status"]
end
def type
@container_info["Type"]
end
def architecture
@container_info["Architecture"]
end
def pid
@container_info["PID"]
end
def created_at
@container_info["Created"]
end
def last_used_at
@container_info["Last Used"]
end
def resources
@container_info["Resources"]
end end
private private
# Method to find lxc def populate_container_info
lxc_util = find_lxc_or_error
lxc_info_cmd = inspec.command("#{lxc_util} info #{@container_name}")
if lxc_info_cmd.exit_status.to_i == 0
parse_command_output(lxc_info_cmd.stdout)
elsif lxc_info_cmd.stderr =~ /Error: Instance not found/
{}
else
raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve information for #{container_name}.\n#{lxc_info_cmd.stderr}"
end
end
def find_lxc_or_error def find_lxc_or_error
%w{/usr/sbin/lxc /sbin/lxc lxc}.each do |cmd| %w{/usr/sbin/lxc /sbin/lxc lxc}.each do |cmd|
return cmd if inspec.command(cmd).exist? return cmd if inspec.command(cmd).exist?
@ -47,11 +102,12 @@ module Inspec::Resources
raise Inspec::Exceptions::ResourceFailed, "Could not find `lxc`" raise Inspec::Exceptions::ResourceFailed, "Could not find `lxc`"
end end
def lxc_info_cmd def parse_command_output(output)
bin = find_lxc_or_error require "yaml" unless defined?(YAML)
info_cmd = "info #{@container_name} | grep -i Status" YAML.load(output)
lxc_cmd = format("%s %s", bin, info_cmd).strip rescue Psych::SyntaxError => e
inspec.command(lxc_cmd) warn "Could not parse the command output.\n#{e.message}"
{}
end end
end end
end end

1
test/fixtures/cmd/lxcerror vendored Normal file
View file

@ -0,0 +1 @@
Error: Instance not found

View file

@ -1 +1,42 @@
Status: Running Name: ubuntu-container
Status: RUNNING
Type: container
Architecture: x86_64
PID: 1378
Created: 2022/08/16 12:07 UTC
Last Used: 2022/08/17 05:06 UTC
Resources:
Processes: 13
Disk usage:
root: 53.97MiB
CPU usage:
CPU usage (in seconds): 2
Memory usage:
Memory (current): 32.30MiB
Network usage:
eth0:
Type: broadcast
State: UP
Host interface: vethc32daefe
MAC address: 00:16:3e:0e:be:4b
MTU: 1500
Bytes received: 18.12kB
Bytes sent: 18.82kB
Packets received: 130
Packets sent: 183
IP addresses:
inet: 10.199.12.205/24 (global)
inet6: fd42:574e:edd0:2d9f:216:3eff:fe0e:be4b/64 (global)
inet6: fe80::216:3eff:fe0e:be4b/64 (link)
lo:
Type: loopback
State: UP
MTU: 65536
Bytes received: 0B
Bytes sent: 0B
Packets received: 0
Packets sent: 0
IP addresses:
inet: 127.0.0.1/8 (local)
inet6: ::1/128 (local)

View file

@ -392,7 +392,8 @@ class MockLoader
"/usr/sbin/ipfstat -io" => cmd.call("ipfstat-io"), "/usr/sbin/ipfstat -io" => cmd.call("ipfstat-io"),
%{type "/usr/sbin/ipfstat"} => empty.call, %{type "/usr/sbin/ipfstat"} => empty.call,
# lxc # lxc
"/usr/sbin/lxc info my-ubuntu-container | grep -i Status" => cmd.call("lxcinfo"), "/usr/sbin/lxc info ubuntu-container" => cmd.call("lxcinfo"),
"/usr/sbin/lxc info my-ubuntu-container-1" => cmd_stderr.call("lxcerror"),
%{sh -c 'type "/usr/sbin/lxc"'} => empty.call, %{sh -c 'type "/usr/sbin/lxc"'} => empty.call,
# cgroup # cgroup
"cgget -n -a carrotking" => cmd.call("cgget-n-a"), "cgget -n -a carrotking" => cmd.call("cgget-n-a"),

View file

@ -8,14 +8,21 @@ require_relative "../../../lib/inspec/resources/lxc"
describe "Inspec::Resources::Lxc" do describe "Inspec::Resources::Lxc" do
# ubuntu # ubuntu
it "verify lxc resource on ubuntu" do it "verify lxc resource on ubuntu" do
resource = MockLoader.new(:ubuntu).load_resource("lxc", "my-ubuntu-container") resource = MockLoader.new(:ubuntu).load_resource("lxc", "ubuntu-container")
_(resource.exists?).must_equal true _(resource.exists?).must_equal true
_(resource.running?).must_equal true _(resource.running?).must_equal true
_(resource.resource_skipped?).must_equal false _(resource.resource_skipped?).must_equal false
_(resource.resource_id).must_equal "my-ubuntu-container" _(resource.name).must_equal "ubuntu-container"
_(resource.status).must_equal "RUNNING"
_(resource.type).must_equal "container"
_(resource.architecture).must_equal "x86_64"
_(resource.pid).must_equal 1378
_(resource.created_at).must_equal "2022/08/16 12:07 UTC"
_(resource.last_used_at).must_equal "2022/08/17 05:06 UTC"
_(resource.resource_id).must_equal "ubuntu-container"
end end
# # ubuntu # ubuntu
it "verify lxc resource on ubuntu for non exisiting container" do it "verify lxc resource on ubuntu for non exisiting container" do
resource = MockLoader.new(:ubuntu).load_resource("lxc", "my-ubuntu-container-1") resource = MockLoader.new(:ubuntu).load_resource("lxc", "my-ubuntu-container-1")
_(resource.exists?).must_equal false _(resource.exists?).must_equal false