Adding docker plugin support (#3074)

* Fixing tests and squashing
* Updating as per some PR comments
* PR comments

Signed-off-by: Noel Georgi <18496730+frezbo@users.noreply.github.com>
This commit is contained in:
Noel Georgi 2018-08-09 17:50:32 +05:30 committed by Jared Quick
parent b872e9135a
commit 9d3beb8d41
9 changed files with 239 additions and 0 deletions

View file

@ -148,6 +148,17 @@ Or execute the profile directly via URL:
its('sizes') { should_not include "1.41 GB" } its('sizes') { should_not include "1.41 GB" }
end end
### plugins
`plugins` returns information about Docker plugins as returned by [docker plugin ls](https://docs.docker.com/engine/reference/commandline/plugin/).
describe docker.plugins do
its('names') { should include ["store/weaveworks/net-plugin", "docker4x/cloudstor"] }
its('ids') { should cmp ["6ea8176de74b", "771d3ee7c7ea"] }
its('versions') { should cmp ["2.3.0", "18.03.1-ce-aws1"] }
its('enabled') { should cmp [true, false] }
end
### info ### info
`info` returns the parsed result of [docker info](https://docs.docker.com/engine/reference/commandline/info/) `info` returns the parsed result of [docker info](https://docs.docker.com/engine/reference/commandline/info/)

View file

@ -0,0 +1,80 @@
---
title: About the docker_plugin Resource
platform: linux
---
# docker_plugin
Use the `docker_plugin` InSpec audit resource to verify a Docker plugin.
<br>
## Syntax
A `docker_plugin` resource block declares the plugin:
describe docker_plugin('rexray/ebs') do
it { should exist }
its('id') { should_not eq '0ac30b93ad40' }
its('version') { should eq '0.11.1' }
it { should be_enabled }
end
<br>
## Resource Parameter Examples
The resource allows you to pass in an plugin id:
describe docker_plugin(id: plugin_id) do
it { should be_enabled }
end
<br>
## Properties
### id
The `id` property returns the full plugin id:
describe docker_plugin('cloudstor/aws') do
its('id') { should eq '0ac30b93ad40' }
end
### version
The `version` property tests the value of plugin version:
describe docker_plugin('cloudstor/aws') do
its('version') { should eq '0.11.0' }
end
## Examples
### Test a Docker plugin
describe docker_plugin('rexray/ebs') do
it { should exist }
its('id') { should_not eq '0ac30b93ad40' }
its('version') { should eq '0.11.1' }
it { should be_enabled }
end
<br>
## Matchers
For a full list of available matchers, please visit our [Universal Matchers](https://www.inspec.io/docs/reference/matchers/).
### exist
The `exist` matcher tests if the plugin is available on the node:
describe docker_plugin('rexray/ebs') do
it { should exist }
end
### enabled
The `be_enabled` matches tests if the plugin is enabled

View file

@ -128,6 +128,7 @@ require 'resources/directory'
require 'resources/docker' require 'resources/docker'
require 'resources/docker_container' require 'resources/docker_container'
require 'resources/docker_image' require 'resources/docker_image'
require 'resources/docker_plugin'
require 'resources/docker_service' require 'resources/docker_service'
require 'resources/elasticsearch' require 'resources/elasticsearch'
require 'resources/etc_fstab' require 'resources/etc_fstab'

View file

@ -52,6 +52,20 @@ module Inspec::Resources
end end
end end
class DockerPluginFilter
filter = FilterTable.create
filter.add(:ids, field: 'id')
.add(:names, field: 'name')
.add(:versions, field: 'version')
.add(:enabled, field: 'enabled')
filter.connect(self, :plugins)
attr_reader :plugins
def initialize(plugins)
@plugins = plugins
end
end
class DockerServiceFilter class DockerServiceFilter
filter = FilterTable.create filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? } filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
@ -89,6 +103,10 @@ module Inspec::Resources
its('repositories') { should_not include 'inssecure_image' } its('repositories') { should_not include 'inssecure_image' }
end end
describe docker.plugins.where { name == 'rexray/ebs' } do
it { should exist }
end
describe docker.services do describe docker.services do
its('images') { should_not include 'inssecure_image' } its('images') { should_not include 'inssecure_image' }
end end
@ -119,6 +137,10 @@ module Inspec::Resources
DockerImageFilter.new(parse_images) DockerImageFilter.new(parse_images)
end end
def plugins
DockerPluginFilter.new(parse_plugins)
end
def services def services
DockerServiceFilter.new(parse_services) DockerServiceFilter.new(parse_services)
end end
@ -226,5 +248,17 @@ module Inspec::Resources
warn 'Could not parse `docker images` output' warn 'Could not parse `docker images` output'
[] []
end end
def parse_plugins
plugins = inspec.command('docker plugin ls --format \'{"id": {{json .ID}}, "name": "{{ with split .Name ":"}}{{index . 0}}{{end}}", "version": "{{ with split .Name ":"}}{{index . 1}}{{end}}", "enabled": {{json .Enabled}} }\'').stdout
c_plugins = []
plugins.each_line { |entry|
c_plugins.push(JSON.parse(entry))
}
c_plugins
rescue JSON::ParserError => _e
warn 'Could not parse `docker plugin ls` output'
[]
end
end end
end end

View file

@ -0,0 +1,63 @@
# encoding: utf-8
module Inspec::Resources
class DockerPlugin < Inspec.resource(1)
name 'docker_plugin'
supports platform: 'unix'
desc 'Retrieves info about docker plugins'
example "
describe docker_plugin('rexray/ebs') do
it { should exist }
its('id') { should_not eq '0ac30b93ad40' }
its('version') { should eq '0.11.1' }
it { should be_enabled }
end
describe docker_plugin('alpine:latest') do
it { should exist }
end
describe docker_plugin(id: '4a415e366388') do
it { should exist }
end
"
def initialize(opts = {})
# do sanitizion of input values
o = opts.dup
o = { name: opts } if opts.is_a?(String)
@opts = o
end
def exist?
object_info.entries.size == 1
end
def enabled?
object_info.enabled[0]
end
def id
object_info.ids[0] if object_info.entries.size == 1
end
def version
object_info.versions[0] if object_info.entries.size == 1
end
def to_s
plugin = @opts[:name] || @opts[:id]
"Docker plugin #{plugin}"
end
private
def object_info
return @info if defined?(@info)
opts = @opts
@info = inspec.docker.plugins.where {
(name == opts[:name]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id]))
}
end
end
end

View file

@ -425,6 +425,8 @@ class MockLoader
"83c36bfade9375ae1feb91023cd1f7409b786fd992ad4013bf0f2259d33d6406" => cmd.call('docker-images'), "83c36bfade9375ae1feb91023cd1f7409b786fd992ad4013bf0f2259d33d6406" => cmd.call('docker-images'),
# docker services # docker services
%{docker service ls --format '{"ID": {{json .ID}}, "Name": {{json .Name}}, "Mode": {{json .Mode}}, "Replicas": {{json .Replicas}}, "Image": {{json .Image}}, "Ports": {{json .Ports}}}'} => cmd.call('docker-service-ls'), %{docker service ls --format '{"ID": {{json .ID}}, "Name": {{json .Name}}, "Mode": {{json .Mode}}, "Replicas": {{json .Replicas}}, "Image": {{json .Image}}, "Ports": {{json .Ports}}}'} => cmd.call('docker-service-ls'),
# docker plugins
%{docker plugin ls --format '{"id": {{json .ID}}, "name": "{{ with split .Name ":"}}{{index . 0}}{{end}}", "version": "{{ with split .Name ":"}}{{index . 1}}{{end}}", "enabled": {{json .Enabled}} }'} => cmd.call('docker-plugin-ls'),
# modprobe for kernel_module # modprobe for kernel_module
"modprobe --showconfig" => cmd.call('modprobe-config'), "modprobe --showconfig" => cmd.call('modprobe-config'),
# get-process cmdlet for processes resource # get-process cmdlet for processes resource

View file

@ -0,0 +1,2 @@
{"id": "6ea8176de74b", "name": "store/weaveworks/net-plugin", "version": "2.3.0", "enabled": true }
{"id": "771d3ee7c7ea", "name": "docker4x/cloudstor", "version": "18.03.1-ce-aws1", "enabled": false }

View file

@ -0,0 +1,39 @@
# encoding: utf-8
# author: Noel Georgi
require 'helper'
require 'inspec/resource'
describe 'Inspec::Resources::DockerContainer' do
describe 'docker_plugin' do
it 'check plugin parsing for docker4x/cloudstor' do
resource = load_resource('docker_plugin', 'docker4x/cloudstor')
_(resource.id).must_equal '771d3ee7c7ea'
_(resource.version).must_equal '18.03.1-ce-aws1'
_(resource.enabled?).must_equal false
_(resource.exist?).must_equal true
end
it 'check plugin parsing for store/weaveworks/net-plugin' do
resource = load_resource('docker_plugin', 'store/weaveworks/net-plugin')
_(resource.id).must_equal '6ea8176de74b'
_(resource.version).must_equal '2.3.0'
_(resource.enabled?).must_equal true
_(resource.exist?).must_equal true
end
it 'check plugin parsing when there are no plugins' do
resource = load_resource('docker_plugin')
assert_nil resource.id
assert_nil resource.version
assert_nil resource.id
assert_nil resource.enabled?
_(resource.exist?).must_equal false
end
it 'prints as a docker resource' do
resource = load_resource('docker_plugin', 'store/weaveworks/net-plugin')
resource.to_s.must_equal 'Docker plugin store/weaveworks/net-plugin'
end
end
end

View file

@ -24,6 +24,13 @@ describe 'Inspec::Resources::Docker' do
_(resource.services.images).must_equal ["foo/image:1.0", "foo/image:1.1", "bar:latest", "bar:latest"] _(resource.services.images).must_equal ["foo/image:1.0", "foo/image:1.1", "bar:latest", "bar:latest"]
end end
it 'check docker plugins parsing' do
_(resource.plugins.ids).must_equal ["6ea8176de74b", "771d3ee7c7ea"]
_(resource.plugins.names).must_equal ["store/weaveworks/net-plugin", "docker4x/cloudstor"]
_(resource.plugins.versions).must_equal ["2.3.0", "18.03.1-ce-aws1"]
_(resource.plugins.enabled).must_equal [true, false]
end
it 'check docker version parsing' do it 'check docker version parsing' do
_(resource.version.Server.Version).must_equal '17.03.0-ce' _(resource.version.Server.Version).must_equal '17.03.0-ce'
_(resource.version.Client.Version).must_equal '17.03.0-ce' _(resource.version.Client.Version).must_equal '17.03.0-ce'