class Inspec::Resources::Docker

This resource helps to parse information from the docker host For compatability with Serverspec we also offer the following resouses:

Public Instance Methods

containers() click to toggle source
# File lib/inspec/resources/docker.rb, line 132
def containers
  DockerContainerFilter.new(parse_containers)
end
images() click to toggle source
# File lib/inspec/resources/docker.rb, line 136
def images
  DockerImageFilter.new(parse_images)
end
info() click to toggle source
# File lib/inspec/resources/docker.rb, line 159
def info
  return @info if defined?(@info)

  data = {}
  # docke info format is only supported for Docker 17.03+
  cmd = inspec.command("docker info --format '{{ json . }}'")
  data = JSON.parse(cmd.stdout) if cmd.exit_status == 0
  @info = Hashie::Mash.new(data)
rescue JSON::ParserError => _e
  Hashie::Mash.new({})
end
object(id) click to toggle source

returns information about docker objects

# File lib/inspec/resources/docker.rb, line 172
def object(id)
  return @inspect if defined?(@inspect)

  data = JSON.parse(inspec.command("docker inspect #{id}").stdout)
  data = data[0] if data.is_a?(Array)
  @inspect = Hashie::Mash.new(data)
rescue JSON::ParserError => _e
  Hashie::Mash.new({})
end
plugins() click to toggle source
# File lib/inspec/resources/docker.rb, line 140
def plugins
  DockerPluginFilter.new(parse_plugins)
end
services() click to toggle source
# File lib/inspec/resources/docker.rb, line 144
def services
  DockerServiceFilter.new(parse_services)
end
to_s() click to toggle source
# File lib/inspec/resources/docker.rb, line 182
def to_s
  "Docker Host"
end
version() click to toggle source
# File lib/inspec/resources/docker.rb, line 148
def version
  return @version if defined?(@version)

  data = {}
  cmd = inspec.command("docker version --format '{{ json . }}'")
  data = JSON.parse(cmd.stdout) if cmd.exit_status == 0
  @version = Hashie::Mash.new(data)
rescue JSON::ParserError => _e
  Hashie::Mash.new({})
end

Private Instance Methods

ensure_keys(entry, labels) click to toggle source
# File lib/inspec/resources/docker.rb, line 242
def ensure_keys(entry, labels)
  labels.each do |key|
    entry[key.downcase] = nil unless entry.key?(key.downcase)
  end
  entry
end
parse_containers() click to toggle source
# File lib/inspec/resources/docker.rb, line 224
def parse_containers
  # @see https://github.com/moby/moby/issues/20625, works for docker 1.13+
  # raw_containers = inspec.command('docker ps -a --no-trunc --format \'{{ json . }}\'').stdout
  # therefore we stick with older approach
  labels = %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status}

  # Networks LocalVolumes work with 1.13+ only
  if !version.empty? && Gem::Version.new(version["Client"]["Version"]) >= Gem::Version.new("1.13")
    labels.push("Networks")
    labels.push("LocalVolumes")
  end
  parse_json_command(labels, "ps -a --no-trunc")
end
parse_images() click to toggle source
# File lib/inspec/resources/docker.rb, line 249
def parse_images
  # docker does not support the `json .` function here, therefore we need to emulate that behavior.
  raw_images = inspec.command('docker images -a --no-trunc --format \'{ "id": {{json .ID}}, "repository": {{json .Repository}}, "tag": {{json .Tag}}, "size": {{json .Size}}, "digest": {{json .Digest}}, "createdat": {{json .CreatedAt}}, "createdsize": {{json .CreatedSince}} }\'').stdout
  c_images = []
  raw_images.each_line do |entry|
    c_images.push(JSON.parse(entry))
  end
  c_images
rescue JSON::ParserError => _e
  warn "Could not parse `docker images` output"
  []
end
parse_json_command(labels, subcommand) click to toggle source
# File lib/inspec/resources/docker.rb, line 188
def parse_json_command(labels, subcommand)
  # build command
  format = labels.map { |label| "\"#{label}\": {{json .#{label}}}" }
  raw = inspec.command("docker #{subcommand} --format '{#{format.join(", ")}}'").stdout
  output = []
  # since docker is not outputting valid json, we need to parse each row
  raw.each_line do |entry|
    # convert all keys to lower_case to work well with ruby and filter table
    row = JSON.parse(entry).map do |key, value|
      [key.downcase, value]
    end.to_h

    # ensure all keys are there
    row = ensure_keys(row, labels)

    # strip off any linked container names
    # Depending on how it was linked, the actual container name may come before
    # or after the link information, so we'll just look for the first name that
    # does not include a slash since that is not a valid character in a container name
    if row["names"]
      row["names"] = row["names"].split(",").find { |c| !c.include?("/") }
    end

    # Split labels on ',' or set to empty array
    # Allows for `docker.containers.where { labels.include?('app=redis') }`
    row["labels"] = row.key?("labels") ? row["labels"].split(",") : []

    output.push(row)
  end

  output
rescue JSON::ParserError => _e
  warn "Could not parse `docker #{subcommand}` output"
  []
end
parse_plugins() click to toggle source
# File lib/inspec/resources/docker.rb, line 262
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 do |entry|
    c_plugins.push(JSON.parse(entry))
  end
  c_plugins
rescue JSON::ParserError => _e
  warn "Could not parse `docker plugin ls` output"
  []
end
parse_services() click to toggle source
# File lib/inspec/resources/docker.rb, line 238
def parse_services
  parse_json_command(%w{ID Name Mode Replicas Image Ports}, "service ls")
end