Class: CemAcpt::Scan::DaemonClient

Inherits:
Object
  • Object
show all
Defined in:
lib/cem_acpt/scan/daemon_client.rb

Overview

HTTP client for the on-node scan daemon. Mirrors the role of Goss::Api: build URIs, GET them, parse the JSON response, turn the response into a typed result.

The daemon is installed by Provision::Linux#scan_provision_commands and serves two endpoints on the configurable scan port:

GET /health -> 200 OK once the daemon has started and the scanner
               binaries it needs are present on disk.
GET /scan   -> 200 with a JSON body shaped like:
                 { "score":  87.4, "passed_count": 187, "failed_count": 27,
                   "not_applicable_count": 14, "error_count": 0, "rules": [...] }
               Non-200 responses or unparseable bodies raise
               {ScannerInvocationError}.

Constant Summary collapse

DEFAULT_PORT =
8084
DEFAULT_READY_TIMEOUT =
60
DEFAULT_HTTP_TIMEOUT =

30 minutes — scans can be long

1800

Instance Method Summary collapse

Constructor Details

#initialize(host:, port: DEFAULT_PORT, ready_timeout: DEFAULT_READY_TIMEOUT, http_timeout: DEFAULT_HTTP_TIMEOUT) ⇒ DaemonClient

Returns a new instance of DaemonClient.

Parameters:

  • host (String)

    The IP or DNS name of the test node.

  • port (Integer) (defaults to: DEFAULT_PORT)

    The port the daemon listens on.

  • ready_timeout (Integer) (defaults to: DEFAULT_READY_TIMEOUT)

    How long to wait for /health.

  • http_timeout (Integer) (defaults to: DEFAULT_HTTP_TIMEOUT)

    How long a single /scan request may take.



34
35
36
37
38
39
# File 'lib/cem_acpt/scan/daemon_client.rb', line 34

def initialize(host:, port: DEFAULT_PORT, ready_timeout: DEFAULT_READY_TIMEOUT, http_timeout: DEFAULT_HTTP_TIMEOUT)
  @host = host
  @port = port
  @ready_timeout = ready_timeout
  @http_timeout = http_timeout
end

Instance Method Details

#scan(test_case:, scanner:, profile:, threshold:) ⇒ Result

Hits /scan and turns the response into a Result.

Parameters:

  • test_case (String)

    Acceptance-test directory name.

  • scanner (Symbol)

    :openscap or :ciscat.

  • profile (String)

    Scanner-native profile id.

  • threshold (Float)

    Pass threshold (0-100).

Returns:

Raises:



66
67
68
69
70
71
72
73
74
# File 'lib/cem_acpt/scan/daemon_client.rb', line 66

def scan(test_case:, scanner:, profile:, threshold:)
  uri = URI("http://#{@host}:#{@port}/scan")
  status, body = get(uri, timeout: @http_timeout)
  unless status.to_i == 200
    raise ScannerInvocationError, "Scan daemon at #{@host}:#{@port} returned status #{status}: #{body.inspect}"
  end

  Result.new(test_case: test_case, scanner: scanner, profile: profile, threshold: threshold, body: body)
end

#wait_until_readyObject

Polls /health until it returns 200 or the timeout elapses.

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/cem_acpt/scan/daemon_client.rb', line 43

def wait_until_ready
  deadline = Time.now + @ready_timeout
  last_error = nil
  while Time.now < deadline
    begin
      return true if get(URI("http://#{@host}:#{@port}/health"), timeout: 5).first.to_i == 200
    rescue StandardError => e
      last_error = e
    end
    sleep 2
  end
  msg = "Scan daemon at #{@host}:#{@port} did not become ready within #{@ready_timeout}s"
  msg += " (last error: #{last_error.class}: #{last_error.message})" if last_error
  raise DaemonNotReadyError, msg
end